# Vaultwarden Deployment Best Practices for Docker, Reverse Proxies, and HTTPS

> Deploy Vaultwarden securely with Docker best practices. Learn essential tips for reverse proxies and HTTPS to protect your Bitwarden instance.

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

---

**Place Vaultwarden behind a reverse proxy for production deployments to handle TLS termination, WebSocket upgrades, and client IP forwarding while keeping the Rust container bound to localhost.**

Vaultwarden is a self-hosted Bitwarden API implementation written in Rust. While the underlying Rocket framework can serve TLS directly, the project explicitly recommends reverse proxy configurations for production environments to manage HTTPS certificates and WebSocket connectivity. Following these **deployment best practices for Docker, reverse proxies, and HTTPS** ensures secure TLS termination, proper live sync functionality, and reliable client IP extraction for audit logs.

## Why Use a Reverse Proxy with Vaultwarden

Running Vaultwarden behind a reverse proxy provides critical production capabilities that direct TLS termination cannot match. According to the official [`README.md`](https://github.com/dani-garcia/vaultwarden/blob/main/README.md), this approach is the recommended architecture for all public-facing instances.

The key advantages include:

- **Flexible TLS management** using Let's Encrypt, custom certificates, or corporate PKI without modifying the Rust binary
- **Reliable WebSocket upgrades** required for live sync and real-time admin diagnostics
- **Security header enforcement** (HSTS, X-Frame-Options, CSP) at the edge
- **Accurate client IP extraction** through configurable header forwarding for rate limiting and audit trails

The `.env.template` file emphasizes that setting the `DOMAIN` variable with an `https://` prefix is intended for use behind a reverse proxy, not for direct Rocket TLS【https://github.com/dani-garcia/vaultwarden/blob/main/.env.template#L195-L202】. Additionally, the admin diagnostics endpoint in [`src/api/admin.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/admin.rs) explicitly checks for proxy presence via environment variables and reports `"uses_proxy": true` when detected【https://github.com/dani-garcia/vaultwarden/blob/main/src/api/admin.rs#L20-L24】.

## Docker Configuration for Production

Bind the Vaultwarden container to localhost only, leaving public exposure to your reverse proxy. This prevents direct container access while allowing the proxy to route traffic securely.

```yaml

# docker-compose.yml

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:80"
    environment:
      - DOMAIN=https://vaultwarden.example.com
      - ADMIN_TOKEN=${ADMIN_TOKEN}
      - IP_HEADER=X-Real-IP
    volumes:
      - ./vw-data:/data

```

**Critical environment variables:**

- **`DOMAIN`**: Must match your public HTTPS URL exactly. The `.env.template` notes this is required for URL generation in invitation emails and API responses【https://github.com/dani-garcia/vaultwarden/blob/main/.env.template#L195-L199】.
- **`IP_HEADER`**: Configures which header Vaultwarden reads for the original client IP. The default `X-Real-IP` matches standard reverse proxy configurations.

## Reverse Proxy Configuration Examples

Choose a proxy that fits your infrastructure. All three options below support automatic HTTPS and WebSocket forwarding.

### NGINX Configuration

NGINX provides fine-grained control over WebSocket handling and security headers. Use Certbot for Let's Encrypt certificates.

```nginx

# /etc/nginx/conf.d/vaultwarden.conf

server {
    listen 80;
    server_name vaultwarden.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name vaultwarden.example.com;

    ssl_certificate /etc/letsencrypt/live/vaultwarden.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vaultwarden.example.com/privkey.pem;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

```

The `X-Real-IP` header must match the `IP_HEADER` value in your Vaultwarden environment. The `Upgrade` and `Connection` headers are mandatory for WebSocket support.

### Caddy Configuration

Caddy offers zero-configuration automatic HTTPS with a simplified configuration format.

```caddy

# Caddyfile

vaultwarden.example.com {
    reverse_proxy 127.0.0.1:8080 {
        header_up X-Real-IP {remote_host}
    }
    encode gzip
}

```

Caddy automatically provisions and renews Let's Encrypt certificates without additional tooling.

### Traefik Configuration

For Docker-native deployments, Traefik uses container labels for dynamic routing.

```yaml
services:
  vaultwarden:
    image: vaultwarden/server:latest
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.rule=Host(`vaultwarden.example.com`)"
      - "traefik.http.routers.vaultwarden.tls=true"
      - "traefik.http.routers.vaultwarden.entrypoints=websecure"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
      - "traefik.http.middlewares.vw-headers.headers.customrequestheaders.X-Real-IP={remoteAddr}"
      - "traefik.http.routers.vaultwarden.middlewares=vw-headers"

```

Traefik handles ACME certificate generation automatically when `tls=true` is specified.

## Verifying Your Deployment

After running `docker compose up -d`, confirm the setup is functioning correctly:

1. Access `https://vaultwarden.example.com` and verify the browser shows a valid TLS certificate
2. Navigate to `/admin/diagnostics` and confirm `"uses_proxy": true` appears in the JSON output, confirming the detection logic in [`src/api/admin.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/admin.rs) recognizes your proxy【https://github.com/dani-garcia/vaultwarden/blob/main/src/api/admin.rs#L20-L24】
3. Open browser developer tools and verify WebSocket connections show `101 Switching Protocols` status

## Security Hardening Recommendations

Beyond basic proxy configuration, enable these additional protections defined in `.env.template`:

- **`ADMIN_RATELIMIT_SECONDS`** and **`ADMIN_RATELIMIT_MAX_BURST`**: Configure brute-force protection for the admin panel. The template suggests `300` seconds with a burst of `3` attempts【https://github.com/dani-garcia/vaultwarden/blob/main/.env.template#L27-L30】
- **`EXTENDED_LOGGING`**: Enable temporarily in [`src/util.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/util.rs) via environment variables to debug proxy header issues when client IPs appear incorrect【https://github.com/dani-garcia/vaultwarden/blob/main/.env.template#L90-L93】
- **`ALLOWED_IFRAME_ANCESTORS`**: Restrict vault embedding to specific domains via CSP headers if required by your security policy

## Summary

- **Always deploy Vaultwarden behind a reverse proxy** in production for proper TLS termination and WebSocket support
- **Bind the Docker container to localhost only** (`127.0.0.1:8080:80`) to prevent direct internet exposure
- **Set `DOMAIN` to your HTTPS URL** and configure `IP_HEADER` to match your proxy's forwarded header
- **Include WebSocket upgrade headers** (`Upgrade` and `Connection`) in your proxy configuration to enable live sync
- **Verify proxy detection** in the admin diagnostics endpoint to confirm proper configuration

## Frequently Asked Questions

### Can Vaultwarden handle HTTPS directly without a reverse proxy?

Yes, but it is not recommended for production. The Rocket web framework supports direct TLS via environment variables, but the [`README.md`](https://github.com/dani-garcia/vaultwarden/blob/main/README.md) explicitly recommends reverse proxies for better certificate management, security header control, and WebSocket reliability. Direct TLS also complicates client IP extraction since the container sees the NAT address rather than the original visitor.

### Which reverse proxy is best for Vaultwarden?

All major proxies work well. **Caddy** requires the least configuration for automatic HTTPS, **NGINX** offers the most granular control over headers and WebSocket tuning, and **Traefik** integrates natively with Docker Compose for dynamic service discovery. Choose based on your existing infrastructure expertise.

### Why are WebSocket headers required in the proxy configuration?

Vaultwarden uses WebSockets for live sync and real-time admin diagnostics. The `Upgrade: websocket` and `Connection: upgrade` headers tell the proxy to transform the HTTP connection into a persistent bidirectional stream. Without these headers, sync functionality will fail silently or fall back to inefficient polling.

### How do I confirm my reverse proxy is properly detected?

Visit the admin diagnostics page at `https://your-domain.com/admin/diagnostics` and check the JSON output for `"uses_proxy": true`. This flag is set by the diagnostics endpoint in [`src/api/admin.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/admin.rs) when it detects proxy-related environment variables or headers, confirming Vaultwarden recognizes it is running behind a proxy and will trust the `IP_HEADER` for client identification.