Skip to main content
Configure a reverse proxy to add SSL/TLS, load balancing, and additional security features to your Open WebUI deployment.

Why Use a Reverse Proxy?

  • SSL/TLS Termination: Handle HTTPS encryption at the proxy layer
  • Load Balancing: Distribute traffic across multiple instances
  • Security: Add authentication, rate limiting, and WAF rules
  • Caching: Improve performance with static asset caching
  • Multiple Services: Host multiple services on different paths/domains

Nginx

Basic Configuration

Create /etc/nginx/sites-available/open-webui:
server {
    listen 80;
    server_name open-webui.example.com;

    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name open-webui.example.com;

    # SSL Configuration
    ssl_certificate /etc/ssl/certs/open-webui.crt;
    ssl_certificate_key /etc/ssl/private/open-webui.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # Security Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Proxy Settings
    location / {
        proxy_pass http://localhost:8080;
        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;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        # WebSocket Support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_connect_timeout 600;
        proxy_send_timeout 600;
        proxy_read_timeout 600;
        send_timeout 600;

        # Buffering
        proxy_buffering off;
        proxy_request_buffering off;
    }

    # File Upload Size
    client_max_body_size 50M;

    # Logging
    access_log /var/log/nginx/open-webui-access.log;
    error_log /var/log/nginx/open-webui-error.log;
}

Enable the Configuration

# Create symbolic link
sudo ln -s /etc/nginx/sites-available/open-webui /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Reload nginx
sudo systemctl reload nginx

With Let’s Encrypt SSL

Use Certbot for free SSL certificates:
1

Install Certbot

sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
2

Obtain Certificate

sudo certbot --nginx -d open-webui.example.com
3

Auto-Renewal

Certbot automatically sets up renewal. Test it:
sudo certbot renew --dry-run

Nginx with Subdirectory

To serve Open WebUI at https://example.com/chat:
location /chat/ {
    proxy_pass http://localhost:8080/;
    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;
    proxy_set_header X-Script-Name /chat;

    # WebSocket Support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Apache

Basic Configuration

Create /etc/apache2/sites-available/open-webui.conf:
<VirtualHost *:80>
    ServerName open-webui.example.com
    
    # Redirect HTTP to HTTPS
    Redirect permanent / https://open-webui.example.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName open-webui.example.com

    # SSL Configuration
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/open-webui.crt
    SSLCertificateKeyFile /etc/ssl/private/open-webui.key
    SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite HIGH:!aNULL:!MD5

    # Security Headers
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-XSS-Protection "1; mode=block"

    # Proxy Configuration
    ProxyPreserveHost On
    ProxyRequests Off
    
    # WebSocket Support
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*)           ws://localhost:8080/$1 [P,L]
    RewriteCond %{HTTP:Upgrade} !=websocket [NC]
    RewriteRule /(.*)           http://localhost:8080/$1 [P,L]

    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/

    # Headers
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"

    # Timeouts
    ProxyTimeout 600

    # File Upload Size
    LimitRequestBody 52428800

    # Logging
    ErrorLog ${APACHE_LOG_DIR}/open-webui-error.log
    CustomLog ${APACHE_LOG_DIR}/open-webui-access.log combined
</VirtualHost>

Enable Required Modules

sudo a2enmod ssl
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
sudo a2enmod headers
sudo a2enmod rewrite

Enable the Site

# Enable the site
sudo a2ensite open-webui

# Test configuration
sudo apache2ctl configtest

# Reload Apache
sudo systemctl reload apache2

Caddy

Caddy automatically handles SSL certificates with Let’s Encrypt:

Basic Configuration

Create Caddyfile:
open-webui.example.com {
    reverse_proxy localhost:8080
    
    # WebSocket support is automatic
    
    # Security Headers
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        X-XSS-Protection "1; mode=block"
    }
    
    # Request size limit
    request_body {
        max_size 50MB
    }
}

Advanced Configuration

open-webui.example.com {
    # Custom TLS
    # tls [email protected]
    
    # Rate limiting (requires caddy-rate-limit plugin)
    # rate_limit {
    #     zone dynamic {
    #         key {remote_host}
    #         events 100
    #         window 1m
    #     }
    # }
    
    # Logging
    log {
        output file /var/log/caddy/open-webui-access.log
    }
    
    reverse_proxy localhost:8080 {
        # Custom headers
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
        
        # Timeouts
        transport http {
            read_timeout 600s
            write_timeout 600s
        }
    }
}

Run Caddy

# Test configuration
caddy validate --config Caddyfile

# Run Caddy
caddy run --config Caddyfile

# Or as a service
sudo systemctl enable caddy
sudo systemctl start caddy

Traefik

Docker Compose with Traefik

docker-compose.yml
version: '3'

services:
  traefik:
    image: traefik:v2.10
    command:
      - --api.dashboard=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --certificatesresolvers.letsencrypt.acme.tlschallenge=true
      - --certificatesresolvers.letsencrypt.acme.email=your-email@example.com
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - web

  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    volumes:
      - open-webui:/app/backend/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.open-webui.rule=Host(`open-webui.example.com`)"
      - "traefik.http.routers.open-webui.entrypoints=websecure"
      - "traefik.http.routers.open-webui.tls.certresolver=letsencrypt"
      - "traefik.http.services.open-webui.loadbalancer.server.port=8080"
      - "traefik.http.middlewares.open-webui-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.routers.open-webui.middlewares=open-webui-headers"
    networks:
      - web
    restart: unless-stopped

volumes:
  open-webui:

networks:
  web:
    external: true

HAProxy

Basic Configuration

haproxy.cfg
global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

    # SSL
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5000
    timeout client 600000
    timeout server 600000

frontend open-webui-frontend
    bind *:80
    bind *:443 ssl crt /etc/ssl/certs/open-webui.pem
    
    # Redirect HTTP to HTTPS
    redirect scheme https code 301 if !{ ssl_fc }
    
    # Security Headers
    http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
    http-response set-header X-Frame-Options "SAMEORIGIN"
    http-response set-header X-Content-Type-Options "nosniff"
    
    # Forward headers
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    http-request set-header X-Forwarded-Port %[dst_port]
    
    default_backend open-webui-backend

backend open-webui-backend
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    
    server open-webui-1 localhost:8080 check
    # Add more servers for load balancing
    # server open-webui-2 localhost:8081 check

Kubernetes Ingress

Nginx Ingress Controller

ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: open-webui
  namespace: open-webui
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - open-webui.example.com
    secretName: open-webui-tls
  rules:
  - host: open-webui.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: open-webui
            port:
              number: 80

Traefik Ingress

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: open-webui
  namespace: open-webui
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`open-webui.example.com`)
      kind: Rule
      services:
        - name: open-webui
          port: 80
  tls:
    certResolver: letsencrypt

Security Considerations

Rate Limiting (Nginx)

http {
    # Define rate limit zone
    limit_req_zone $binary_remote_addr zone=openwebui_limit:10m rate=10r/s;
    
    server {
        location / {
            # Apply rate limiting
            limit_req zone=openwebui_limit burst=20 nodelay;
            
            proxy_pass http://localhost:8080;
            # ... other proxy settings
        }
    }
}

IP Whitelisting (Nginx)

location / {
    # Allow specific IPs
    allow 192.168.1.0/24;
    allow 10.0.0.0/8;
    deny all;
    
    proxy_pass http://localhost:8080;
}

Basic Authentication (Nginx)

# Create password file
sudo htpasswd -c /etc/nginx/.htpasswd username
location / {
    auth_basic "Restricted Access";
    auth_basic_user_file /etc/nginx/.htpasswd;
    
    proxy_pass http://localhost:8080;
}

Environment Variables for Proxied Setup

When behind a reverse proxy, configure these environment variables:
# Trust forwarded headers from proxy
FORWARDED_ALLOW_IPS=*

# For HTTPS behind proxy
WEBUI_SESSION_COOKIE_SECURE=true
WEBUI_AUTH_COOKIE_SECURE=true

Troubleshooting

WebSocket Connection Issues

Ensure WebSocket upgrade headers are set:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Large File Uploads Failing

Increase client body size:
client_max_body_size 50M;

Timeout Errors

Increase proxy timeouts:
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;

Check Proxy Logs

# Nginx
sudo tail -f /var/log/nginx/error.log

# Apache
sudo tail -f /var/log/apache2/error.log

# Caddy
sudo journalctl -u caddy -f

Next Steps

Environment Variables

Configure security and performance settings

Kubernetes

Kubernetes Ingress configuration

Docker

Docker deployment guide

Updating

Keep your deployment updated

Build docs developers (and LLMs) love