Compatibility Considerations When Using Official Bitwarden Clients with Vaultwarden

Vaultwarden parses the Bitwarden-Client-Version header from every request to gate features, ensuring older official clients receive filtered API responses while newer clients access advanced capabilities like SSH key support.

Vaultwarden implements the Bitwarden Client API as a drop-in replacement for the official service, maintaining seamless compatibility through version detection and adaptive response filtering. Understanding these compatibility considerations when using official Bitwarden clients with Vaultwarden helps administrators troubleshoot client behavior and predict feature availability across different client versions.

Client Version Detection

Vaultwarden extracts the client version from every incoming request to determine which features the client can safely handle. In src/auth.rs, the server implements a FromRequest guard that parses the Bitwarden-Client-Version header into a ClientVersion struct using semantic versioning:

// src/auth.rs
#[rocket::async_trait]
impl<'r> FromRequest<'r> for ClientVersion {
    type Error = &'static str;

    async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
        let headers = request.headers();

        // The official Bitwarden apps always send this header.
        let Some(version) = headers.get_one("Bitwarden-Client-Version") else {
            err_handler!("No Bitwarden-Client-Version header provided")
        };

        // Validate it follows semver.
        let Ok(version) = semver::Version::parse(version) else {
            err_handler!("Invalid Bitwarden-Client-Version header provided")
        };

        Outcome::Success(ClientVersion(version))
    }
}

The extracted ClientVersion is passed as an optional parameter to route handlers throughout the codebase, including sync, identity, and cipher-related endpoints. This allows Vaultwarden to adjust its behavior dynamically based on the client's reported version.

Version-Gated Feature Support

To prevent JSON parsing errors on older clients, Vaultwarden hides features that exceed a client's understood schema. The most prominent example occurs in the /sync endpoint within src/api/core/ciphers.rs, where SSH keys are filtered out for clients older than version 2024.12.0:

// src/api/core/ciphers.rs
#[get("/sync?<data..>")]
async fn sync(
    data: SyncData,
    headers: Headers,
    client_version: Option<ClientVersion>,
    conn: DbConn,
) -> JsonResult {
    // ...

    // Filter out SSH keys if the client version is less than 2024.12.0
    let show_ssh_keys = if let Some(client_version) = client_version {
        let ver_match = semver::VersionReq::parse(">=2024.12.0").unwrap();
        ver_match.matches(&client_version.0)
    } else {
        false
    };
    if !show_ssh_keys {
        ciphers.retain(|c| c.atype != 5); // 5 = SSH key type
    }

    // ...
}

Clients reporting versions prior to 2024.12.0 receive sync responses that omit all cipher objects where "type": 5, effectively hiding SSH key support without breaking the client's vault decryption or sync process.

Experimental Client Feature Flags

Administrators can toggle experimental features via the EXPERIMENTAL_CLIENT_FEATURE_FLAGS environment variable. In src/util.rs, the parse_experimental_client_feature_flags function processes these flags while filtering out deprecated values to maintain backward compatibility with legacy installations:

// src/util.rs
/// Parses the experimental client feature flags string into a HashMap.
pub fn parse_experimental_client_feature_flags(
    experimental_client_feature_flags: &str,
) -> HashMap<String, bool> {
    // Deprecated flags are filtered out to keep old installations from breaking.
    const DEPRECATED_FLAGS: &[&str] = &[
        "autofill-overlay",
        "autofill-v2",
        "browser-fileless-import",
        "extension-refresh",
        "fido2-vault-credentials",
    ];
    experimental_client_feature_flags
        .split(',')
        .filter_map(|f| {
            let flag = f.trim();
            if !flag.is_empty() && !DEPRECATED_FLAGS.contains(&flag) {
                return Some((flag.to_owned(), true));
            }
            None
        })
        .collect()
}

The resulting flag map is consulted in src/api/core/mod.rs and other locations to conditionally enable behavior without exposing unstable features to incompatible clients.

Legacy Protocol Support

Beyond version gating, Vaultwarden maintains compatibility through protocol-specific adaptations:

U2F Legacy Support: In src/api/core/two_factor/webauthn.rs, the server adds an appid field only when necessary to support legacy U2F hardware keys, ensuring older authenticators continue to function with modern WebAuthn flows.

Identity Flow Adaptation: The src/api/identity.rs handlers propagate client_version through login and two-factor authentication processes, influencing SSO redirect behavior and two-factor method availability based on the client's capabilities.

Icon Download Compatibility: Special handling in src/util.rs addresses edge cases where specific clients, such as the Bitwarden Desktop application, fail to download icons under certain conditions, preventing 404 errors that would otherwise disrupt the user experience.

Graceful Degradation Strategy

When Vaultwarden encounters a feature request from an incompatible client, it employs field omission rather than error emission. The server simply excludes unsupported fields from JSON payloads, maintaining the seamless experience expected from the official Bitwarden service. No error codes are returned for version mismatches.

For truly unsupported API routes or malformed requests, Vaultwarden returns identical HTTP status codes (404, 400, 401, etc.) to those returned by the official Bitwarden servers, ensuring client-side error handling behaves identically whether connected to Vaultwarden or the official cloud service.

Testing Client Compatibility

To verify version-specific behavior, you can simulate different client versions using the Bitwarden-Client-Version header:


# Request sync as Android client v2024.12.0 (receives SSH keys)

curl -X GET "https://my-vaultwarden-instance.com/api/sync" \
     -H "Authorization: Bearer <access_token>" \
     -H "Bitwarden-Client-Version: 2024.12.0"

# Request sync as older client v2024.10.0 (excludes SSH keys)

curl -X GET "https://my-vaultwarden-instance.com/api/sync" \
     -H "Authorization: Bearer <access_token>" \
     -H "Bitwarden-Client-Version: 2024.10.0"

To enable experimental features for testing:


# docker-compose.yml

services:
  vaultwarden:
    image: vaultwarden/server:latest
    environment:
      - EXPERIMENTAL_CLIENT_FEATURE_FLAGS=new-password-generator

Summary

  • Version Extraction: Vaultwarden reads the Bitwarden-Client-Version header in src/auth.rs to determine client capabilities using semantic versioning.
  • Feature Gating: Advanced features like SSH keys (type 5) are filtered from responses when client_version reports versions older than 2024.12.0.
  • Flag Management: The EXPERIMENTAL_CLIENT_FEATURE_FLAGS environment variable controls beta features, with deprecated flags automatically filtered in src/util.rs.
  • Silent Compatibility: Unsupported features are omitted from JSON responses rather than returning errors, preserving the official Bitwarden client experience.
  • Protocol Adaptation: Legacy U2F appid fields and icon download workarounds ensure older hardware and specific clients function correctly.

Frequently Asked Questions

How does Vaultwarden handle outdated Bitwarden clients?

Vaultwarden inspects the Bitwarden-Client-Version header and filters response payloads to exclude features the client cannot parse. For example, clients older than version 2024.12.0 receive sync data with SSH keys removed, preventing JSON deserialization errors while allowing the vault to function normally.

Can I force-enable features for older clients?

No. While you can set EXPERIMENTAL_CLIENT_FEATURE_FLAGS to enable beta functionality, Vaultwarden's core compatibility logic—such as the SSH key filtering in src/api/core/ciphers.rs—uses hardcoded version requirements to prevent breaking older clients. Feature flags are intended for testing new Bitwarden features, not bypassing version checks.

What happens if the Bitwarden-Client-Version header is missing?

The ClientVersion extractor in src/auth.rs returns an error outcome if the header is absent or contains an invalid semver string. However, all official Bitwarden clients (desktop, mobile, browser extensions, and CLI) reliably send this header, so missing headers typically indicate non-standard or automated API requests rather than legitimate client connections.

Are there compatibility differences between Bitwarden Desktop and mobile clients?

Yes. Specific edge cases exist for different client types. For instance, src/util.rs contains special handling for Bitwarden Desktop icon downloads, and the WebAuthn implementation in src/api/core/two_factor/webauthn.rs adds legacy U2F appid fields for clients that require them. These adaptations ensure consistent functionality across the diverse Bitwarden client ecosystem.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →