Reverse Proxy Configuration for Web Services

6 October 2025 · Updated 6 October 2025

reverse-proxynginxcaddytraefiksslweb-server

Configuration guides for popular reverse proxy solutions (NGINX, Caddy, Traefik) used with self-hosted web applications.

NGINX Configuration

Basic Reverse Proxy

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

    # SSL configuration
    ssl_certificate /etc/letsencrypt/live/vault.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vault.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

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

    client_max_body_size 525M;

    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;
    }
}

# HTTP redirect
server {
    listen 80;
    server_name vault.example.com;
    return 301 https://$server_name$request_uri;
}

WebSocket Support

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

    # WebSocket support for Vaultwarden
    location /notifications/hub {
        proxy_pass http://localhost:3012;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;
    }

    location /notifications/hub/negotiate {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Security Hardening

# Rate limiting
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

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

    # Restrict admin panel by IP
    location /admin {
        allow 192.168.1.0/24;
        deny all;

        proxy_pass http://localhost:8080;
        # ... other proxy settings
    }

    # Rate limit login endpoints
    location /identity/connect/token {
        limit_req zone=login burst=5 nodelay;
        proxy_pass http://localhost:8080;
    }
}

Caddy Configuration

Basic Setup (Automatic HTTPS)

Caddyfile:

vault.example.com {
    reverse_proxy localhost:8080 {
        header_up X-Real-IP {remote_host}
    }

    reverse_proxy /notifications/hub localhost:3012
}

Caddy handles SSL with Let’s Encrypt automatically.

Advanced Configuration

vault.example.com {
    # Custom TLS
    tls admin@example.com {
        protocols tls1.2 tls1.3
    }

    # Security headers
    header {
        Strict-Transport-Security "max-age=31536000"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "SAMEORIGIN"
    }

    # File upload limit
    request_body {
        max_size 525MB
    }

    # Main application
    reverse_proxy localhost:8080 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }

    # WebSocket
    reverse_proxy /notifications/hub localhost:3012
}

IP Restriction

vault.example.com {
    @admin {
        path /admin*
        not remote_ip 192.168.1.0/24
    }

    respond @admin 403

    reverse_proxy localhost:8080
}

Traefik Configuration

version: '3'

services:
  vaultwarden:
    image: vaultwarden/server:latest
    labels:
      # Enable Traefik
      - "traefik.enable=true"

      # Main application
      - "traefik.http.routers.vaultwarden.rule=Host(`vault.example.com`)"
      - "traefik.http.routers.vaultwarden.entrypoints=websecure"
      - "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"

      # WebSocket
      - "traefik.http.routers.vaultwarden-ws.rule=Host(`vault.example.com`) && Path(`/notifications/hub`)"
      - "traefik.http.routers.vaultwarden-ws.entrypoints=websecure"
      - "traefik.http.routers.vaultwarden-ws.tls.certresolver=letsencrypt"
      - "traefik.http.services.vaultwarden-ws.loadbalancer.server.port=3012"
    networks:
      - traefik

networks:
  traefik:
    external: true

Static Configuration (traefik.yml)

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@example.com
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: web

Dynamic Configuration (File Provider)

http:
  routers:
    vaultwarden:
      rule: "Host(`vault.example.com`)"
      entryPoints:
        - websecure
      service: vaultwarden
      tls:
        certResolver: letsencrypt

  services:
    vaultwarden:
      loadBalancer:
        servers:
          - url: "http://localhost:8080"

SSL/TLS Configuration

Let’s Encrypt with Certbot

# Install certbot
sudo apt install certbot python3-certbot-nginx

# Obtain certificate
sudo certbot --nginx -d vault.example.com

# Auto-renewal
sudo certbot renew --dry-run

Manual Certificate Installation

NGINX:

ssl_certificate /etc/ssl/certs/vault.example.com.crt;
ssl_certificate_key /etc/ssl/private/vault.example.com.key;
ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;

Caddy:

vault.example.com {
    tls /etc/ssl/certs/vault.crt /etc/ssl/private/vault.key
    reverse_proxy localhost:8080
}

Load Balancing

NGINX Upstream

upstream vaultwarden_backend {
    least_conn;
    server 192.168.1.10:8080 weight=1;
    server 192.168.1.11:8080 weight=1;
    server 192.168.1.12:8080 backup;
}

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

    location / {
        proxy_pass http://vaultwarden_backend;
        proxy_set_header Host $host;
    }
}

Traefik Weighted Load Balancing

services:
  vaultwarden:
    labels:
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
      - "traefik.http.services.vaultwarden.loadbalancer.sticky.cookie=true"

Troubleshooting

NGINX Issues

Test configuration:

sudo nginx -t
sudo systemctl reload nginx

View logs:

tail -f /var/log/nginx/error.log
tail -f /var/log/nginx/access.log

Caddy Issues

Check configuration:

caddy validate --config /etc/caddy/Caddyfile
caddy reload --config /etc/caddy/Caddyfile

View logs:

journalctl -u caddy -f

Traefik Issues

Debug mode:

log:
  level: DEBUG

Access logs:

accessLog:
  filePath: "/var/log/traefik/access.log"