# How the Vaultwarden Web Vault Works: Static Asset Serving and Experimental Client Feature Flags

> Discover how the Vaultwarden Web Vault serves static assets and enables experimental client features through feature flags. Learn to control the Bitwarden UI with environment variables for seamless operation.

- Repository: [Daniel García/vaultwarden](https://github.com/dani-garcia/vaultwarden)
- Tags: deep-dive
- Published: 2026-03-07

---

**Vaultwarden bundles a self-contained Web Vault that serves the Bitwarden-compatible UI from the same binary, controlled by `WEB_VAULT_ENABLED` and `WEB_VAULT_FOLDER` environment variables, while experimental client feature flags enable incremental rollouts via a comma-separated list parsed into a HashMap exposed through the `/api/config` endpoint.**

The Vaultwarden project (`dani-garcia/vaultwarden`) ships a lightweight, Rust-powered server that replaces Bitwarden's heavy dependencies with a single binary. Understanding how the Vaultwarden Web Vault serves static assets and how experimental client feature flags unlock beta functionality is essential for administrators customizing their deployment.

## Enabling and Serving the Vaultwarden Web Vault

### Configuration Parameters

In [`src/config.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/config.rs), the `make_config!` macro generates two critical settings for the Web Vault. The **`web_vault_enabled`** boolean defaults to `true` and reads from the `WEB_VAULT_ENABLED` environment variable. The **`web_vault_folder`** string defaults to `web-vault/` and maps to `WEB_VAULT_FOLDER`. These values determine whether the UI activates and where the server looks for static files.

### Startup Validation

Before accepting connections, the **`check_web_vault()`** function in [`src/main.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/main.rs) verifies that [`index.html`](https://github.com/dani-garcia/vaultwarden/blob/main/index.html) exists inside the configured folder. If the file is missing, the process aborts immediately with an error message directing administrators to the project wiki for build instructions.

### Routing and Static Asset Delivery

All HTTP routes for the UI are assembled in [`src/api/web.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/web.rs). When `web_vault_enabled` is true, the router registers handlers including **`web_index`** (serves [`index.html`](https://github.com/dani-garcia/vaultwarden/blob/main/index.html)), **`web_files`** (serves CSS, JS, and images), and **`vaultwarden_css`** (serves compiled styles). Both `web_index` and `web_files` wrap their responses in the **`Cached`** utility to set HTTP cache headers.

### Dynamic CSS Generation

The server generates CSS on-the-fly from [`scss/vaultwarden.scss`](https://github.com/dani-garcia/vaultwarden/blob/main/scss/vaultwarden.scss), injecting runtime configuration values such as signup availability and WebAuthn support. This templating approach allows the Web Vault UI to adapt to server settings without requiring a rebuild of the static JavaScript bundle.

### Cache Control Strategy

The **`Cached`** wrapper in [`src/util.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/util.rs) stores handler results for a configurable TTL. The Web Vault uses a short TTL for [`index.html`](https://github.com/dani-garcia/vaultwarden/blob/main/index.html) to ensure configuration changes reflect quickly, while the compiled CSS receives a one-day TTL for optimal performance.

## Experimental Client Feature Flags

### Configuration and Parsing

The raw flag string is stored in **`experimental_client_feature_flags`** (default empty) in [`src/config.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/config.rs), populated from the `EXPERIMENTAL_CLIENT_FEATURE_FLAGS` environment variable. The helper **`parse_experimental_client_feature_flags`** in [`src/util.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/util.rs) converts the comma-separated string into a `HashMap<String,bool>`. This parser silently drops deprecated flags like `autofill-overlay` to prevent legacy configurations from breaking.

### Validation and API Exposure

The API core layer in [`src/api/core/mod.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/mod.rs) calls the parser to validate that only recognized flags are present, emitting an error if an unknown flag is supplied. The parsed map is exposed through the `/api/config` endpoint and injected into template contexts, enabling both API consumers and the Web Vault UI to detect enabled features.

### Client-Side Usage

Clients can check for flags such as `enable-new-login-flow` or `new-sync-algorithm`. The server automatically makes these flags available to Handlebars templates and API responses, allowing feature-gated rollouts without deploying a new server version.

## Practical Configuration Examples

Enable the Web Vault in a Docker Compose file:

```yaml
services:
  vaultwarden:
    image: vaultwarden/server:latest
    environment:
      - WEB_VAULT_ENABLED=true
      - WEB_VAULT_FOLDER=/custom/vault
    volumes:
      - ./custom-vault:/custom/vault

```

Activate experimental flags before starting the server:

```bash
export EXPERIMENTAL_CLIENT_FEATURE_FLAGS="new-sync-algorithm,quick-unlock"
./vaultwarden

```

Query the current flag state via the API:

```bash
curl -s https://vault.example.com/api/config | jq '.experimental_client_feature_flags'

```

## Summary

- The **Web Vault** is controlled by `WEB_VAULT_ENABLED` and serves files from `WEB_VAULT_FOLDER`, with startup validation in [`src/main.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/main.rs) via `check_web_vault()`.
- Static assets are routed through handlers in [`src/api/web.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/web.rs) using the **`Cached`** wrapper from [`src/util.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/util.rs) to optimize delivery.
- **Experimental client feature flags** are parsed from comma-separated strings in [`src/util.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/util.rs) by `parse_experimental_client_feature_flags` and validated in [`src/api/core/mod.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/mod.rs).
- Flags are exposed via `/api/config` and template contexts to enable gradual feature rollouts without binary updates.

## Frequently Asked Questions

### What happens if the Web Vault folder is missing at startup?

The server calls `check_web_vault()` in [`src/main.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/main.rs), detects the missing [`index.html`](https://github.com/dani-garcia/vaultwarden/blob/main/index.html), and aborts with a clear error message pointing to the build documentation. This ensures administrators cannot accidentally run a broken UI.

### Can I disable the Web Vault and use only the API?

Yes. Set `WEB_VAULT_ENABLED=false` to run Vaultwarden as a headless API server. This configuration is useful for mobile-only deployments or when serving the UI through a separate reverse proxy or CDN.

### How do I know which experimental flags are available?

Check the official Vaultwarden documentation or review the validation logic in [`src/api/core/mod.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/mod.rs) and the parser in [`src/util.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/util.rs). The server maintains a known list of valid flags and rejects unknown entries with an error on startup.

### Do experimental flags persist across restarts?

Yes, because they are read from the `EXPERIMENTAL_CLIENT_FEATURE_FLAGS` environment variable each time the server initializes. The parsed `HashMap` is rebuilt on every launch, ensuring consistent behavior until you modify the environment variable.