> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/kyutai-labs/unmute/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTPS Configuration

> Enable HTTPS for Unmute to allow microphone access without SSH tunneling

HTTPS is essential for production Unmute deployments because browsers require a secure connection (HTTPS) or localhost to grant microphone access. This guide explains how to add HTTPS support to your Unmute deployment.

## Why HTTPS?

Modern browsers enforce strict security policies:

* **Microphone access** requires either:
  * HTTPS connection with valid certificate, OR
  * Localhost connection (any protocol)

* **WebSocket connections** for real-time audio work better over HTTPS

<Warning>
  HTTP deployments (without localhost) will be blocked by browser security policies from accessing the microphone.
</Warning>

## Recommended Approach: Docker Swarm

The easiest way to get HTTPS is to use the [Docker Swarm deployment](/deployment/docker-swarm), which includes:

* **Automatic HTTPS** via Let's Encrypt
* **Certificate renewal** handled automatically
* **Traefik reverse proxy** configured for HTTPS
* **HTTP to HTTPS redirect**

From `swarm-deploy.yml`:

```yaml swarm-deploy.yml theme={null}
traefik:
  command:
    # HTTP and HTTPS entry points
    - "--entrypoints.web.address=:80"
    - "--entrypoints.websecure.address=:443"
    
    # Automatic redirect HTTP → HTTPS
    - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
    - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
    
    # Let's Encrypt automatic certificate management
    - "--certificatesResolvers.letsencrypt_resolver.acme.httpChallenge.entryPoint=web"
    - "--certificatesResolvers.letsencrypt_resolver.acme.storage=/letsencrypt/acme.json"
    - "--certificatesResolvers.letsencrypt_resolver.acme.email=your-email@example.com"
```

Services automatically get HTTPS:

```yaml swarm-deploy.yml theme={null}
frontend:
  deploy:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`unmute.example.com`)"
      - "traefik.http.routers.frontend.entrypoints=websecure"
      - "traefik.http.routers.frontend.tls=true"
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt_resolver"
```

<Note>
  If you're using Docker Swarm, HTTPS is already configured. No additional setup needed.
</Note>

## Manual HTTPS Setup

For Docker Compose or Dockerless deployments, you'll need to add HTTPS support manually.

### Prerequisites

* **Domain name** pointing to your server's public IP
* **Ports 80 and 443** open in your firewall
* **Public IP address** (Let's Encrypt needs to verify domain ownership)

### Option 1: Add HTTPS to Docker Compose

Modify your `docker-compose.yml` to enable HTTPS with Let's Encrypt.

<Steps>
  <Step title="Update Traefik Configuration">
    Edit the `traefik` service in `docker-compose.yml`:

    ```yaml docker-compose.yml theme={null}
    traefik:
      image: traefik:v3.3.1
      command:
        - "--providers.docker=true"
        - "--providers.docker.exposedbydefault=false"
        
        # Add HTTPS support
        - "--entrypoints.web.address=:80"
        - "--entrypoints.websecure.address=:443"
        - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
        - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
        
        # Let's Encrypt configuration
        - "--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=web"
        - "--certificatesResolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
        - "--certificatesResolvers.letsencrypt.acme.email=your-email@example.com"
      ports:
        - "80:80"
        - "443:443"  # Add HTTPS port
      volumes:
        - "/var/run/docker.sock:/var/run/docker.sock:ro"
        - "letsencrypt:/letsencrypt"  # Persist certificates
    ```
  </Step>

  <Step title="Update Service Labels">
    Update frontend and backend services to use HTTPS:

    ```yaml docker-compose.yml theme={null}
    frontend:
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.frontend.rule=Host(`unmute.example.com`)"
        - "traefik.http.routers.frontend.entrypoints=websecure"
        - "traefik.http.routers.frontend.tls=true"
        - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"
        - "traefik.http.services.frontend.loadbalancer.server.port=3000"
        - "traefik.http.routers.frontend.priority=10"

    backend:
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.backend.rule=Host(`unmute.example.com`) && PathPrefix(`/api`)"
        - "traefik.http.routers.backend.entrypoints=websecure"
        - "traefik.http.routers.backend.tls=true"
        - "traefik.http.routers.backend.tls.certresolver=letsencrypt"
        - "traefik.http.routers.backend.middlewares=strip-api"
        - "traefik.http.middlewares.strip-api.replacepathregex.regex=^/api/(.*)"
        - "traefik.http.middlewares.strip-api.replacepathregex.replacement=/$$1"
        - "traefik.http.services.backend.loadbalancer.server.port=80"
        - "traefik.http.routers.backend.priority=100"
    ```
  </Step>

  <Step title="Add Volume for Certificates">
    Add the letsencrypt volume at the end of `docker-compose.yml`:

    ```yaml docker-compose.yml theme={null}
    volumes:
      letsencrypt:
    ```
  </Step>

  <Step title="Deploy with HTTPS">
    ```bash theme={null}
    docker compose down
    docker compose up -d
    ```

    Access your deployment at `https://unmute.example.com`
  </Step>
</Steps>

<Note>
  Let's Encrypt certificates are valid for 90 days. Traefik automatically renews them before expiration.
</Note>

### Option 2: Nginx Reverse Proxy

Use Nginx as a reverse proxy with Let's Encrypt certificates.

<Steps>
  <Step title="Install Certbot">
    ```bash theme={null}
    sudo apt update
    sudo apt install certbot python3-certbot-nginx
    ```
  </Step>

  <Step title="Obtain Certificate">
    ```bash theme={null}
    sudo certbot certonly --nginx -d unmute.example.com
    ```

    Certificates will be saved to `/etc/letsencrypt/live/unmute.example.com/`
  </Step>

  <Step title="Configure Nginx">
    Create `/etc/nginx/sites-available/unmute`:

    ```nginx /etc/nginx/sites-available/unmute theme={null}
    # Redirect HTTP to HTTPS
    server {
        listen 80;
        server_name unmute.example.com;
        return 301 https://$server_name$request_uri;
    }

    # HTTPS server
    server {
        listen 443 ssl http2;
        server_name unmute.example.com;
        
        # SSL certificate
        ssl_certificate /etc/letsencrypt/live/unmute.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/unmute.example.com/privkey.pem;
        
        # SSL configuration
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;
        
        # Proxy to Docker Compose (Traefik on port 80)
        location / {
            proxy_pass http://localhost:80;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            
            # WebSocket support
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
    ```
  </Step>

  <Step title="Enable and Restart Nginx">
    ```bash theme={null}
    sudo ln -s /etc/nginx/sites-available/unmute /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl restart nginx
    ```
  </Step>

  <Step title="Set Up Auto-Renewal">
    Certbot installs a cron job automatically, but verify:

    ```bash theme={null}
    sudo certbot renew --dry-run
    ```
  </Step>
</Steps>

### Option 3: Caddy (Automatic HTTPS)

Caddy is the simplest option - it automatically obtains and renews certificates.

<Steps>
  <Step title="Install Caddy">
    ```bash theme={null}
    sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
    sudo apt update
    sudo apt install caddy
    ```
  </Step>

  <Step title="Configure Caddyfile">
    Edit `/etc/caddy/Caddyfile`:

    ```caddy /etc/caddy/Caddyfile theme={null}
    unmute.example.com {
        reverse_proxy localhost:80
    }
    ```

    That's it! Caddy automatically:

    * Obtains Let's Encrypt certificate
    * Configures HTTPS
    * Redirects HTTP to HTTPS
    * Renews certificates
  </Step>

  <Step title="Restart Caddy">
    ```bash theme={null}
    sudo systemctl restart caddy
    ```
  </Step>
</Steps>

<Note>
  Caddy is the easiest option for automatic HTTPS. No certificate management needed.
</Note>

## Dockerless HTTPS Setup

For Dockerless deployments, you need to configure the frontend and backend separately.

### Option: Nginx for Dockerless

```nginx /etc/nginx/sites-available/unmute theme={null}
server {
    listen 443 ssl http2;
    server_name unmute.example.com;
    
    ssl_certificate /etc/letsencrypt/live/unmute.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/unmute.example.com/privkey.pem;
    
    # Frontend (Next.js on port 3000)
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    
    # Backend API (FastAPI on port 8000)
    location /api/ {
        rewrite ^/api/(.*) /$1 break;
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name unmute.example.com;
    return 301 https://$server_name$request_uri;
}
```

## Testing HTTPS Configuration

<Steps>
  <Step title="Verify Certificate">
    ```bash theme={null}
    curl -I https://unmute.example.com
    ```

    Should show `HTTP/2 200` or `HTTP/1.1 200`
  </Step>

  <Step title="Test SSL Configuration">
    Use [SSL Labs](https://www.ssllabs.com/ssltest/) to check your configuration:

    ```
    https://www.ssllabs.com/ssltest/analyze.html?d=unmute.example.com
    ```

    Aim for an A or A+ rating.
  </Step>

  <Step title="Verify Microphone Access">
    Open `https://unmute.example.com` in your browser and test that:

    * Page loads correctly
    * No certificate warnings
    * Microphone permission prompt appears
  </Step>
</Steps>

## Troubleshooting

### Certificate Not Issued

**Issue**: Let's Encrypt fails to issue certificate

**Solutions**:

* Verify domain points to your server: `nslookup unmute.example.com`
* Check ports 80 and 443 are open: `sudo ufw status`
* Ensure no other service is using port 80/443
* Check Let's Encrypt logs: `sudo journalctl -u certbot`

### Mixed Content Errors

**Issue**: Browser shows mixed content warnings

**Solution**: Ensure all resources (API calls, WebSockets) use HTTPS URLs, not HTTP.

### WebSocket Connection Fails

**Issue**: WebSocket connections fail over HTTPS

**Solutions**:

* Verify proxy passes `Upgrade` header:
  ```nginx theme={null}
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  ```
* Check for timeout settings - WebSocket connections are long-lived
* Test WebSocket directly: `wscat -c wss://unmute.example.com/api/ws`

### Certificate Renewal Fails

**Issue**: Certificate expired or auto-renewal failed

**Solutions**:

* Test renewal: `sudo certbot renew --dry-run`
* Check renewal cron job: `sudo systemctl status certbot.timer`
* Manually renew: `sudo certbot renew`
* Restart web server after renewal

## Security Best Practices

<Warning>
  Follow these security practices for production deployments:
</Warning>

### Strong SSL Configuration

Use modern TLS versions and ciphers:

```nginx theme={null}
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
```

### HSTS (HTTP Strict Transport Security)

Force browsers to always use HTTPS:

```nginx theme={null}
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
```

### Certificate Monitoring

Set up monitoring to alert before certificate expiration:

```bash theme={null}
# Check expiration
sudo certbot certificates

# Days until expiration
openssl x509 -enddate -noout -in /etc/letsencrypt/live/unmute.example.com/fullchain.pem
```

## Alternative: SSH Tunneling

If setting up HTTPS is too complex, consider using [SSH port forwarding](/deployment/remote-access) instead:

* No domain name required
* No certificate management
* Works immediately
* Suitable for personal/development use

HTTPS is recommended for production, but SSH tunneling is easier for testing.

## Next Steps

* Review [Docker Swarm deployment](/deployment/docker-swarm) for automatic HTTPS
* Learn about [remote access](/deployment/remote-access) via SSH tunneling
* Explore [Docker Compose deployment](/deployment/docker-compose) basics


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.