Skip to content

Reverse Proxy Configuration

Overview

This guide covers configuring popular reverse proxies for Readur, including SSL/TLS setup, performance optimization, and security hardening.

NGINX Configuration

Basic HTTPS Setup

# /etc/nginx/sites-available/readur
server {
    listen 80;
    server_name readur.company.com;

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

server {
    listen 443 ssl http2;
    server_name readur.company.com;

    # SSL Configuration
    ssl_certificate /etc/ssl/certs/readur.crt;
    ssl_certificate_key /etc/ssl/private/readur.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # 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;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Proxy Configuration
    location / {
        proxy_pass http://localhost:8000;
        proxy_http_version 1.1;

        # Headers
        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;

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Buffering
        proxy_buffering off;
        proxy_request_buffering off;
    }

    # WebSocket support for real-time features
    location /ws {
        proxy_pass http://localhost:8000/ws;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # Large file uploads
    client_max_body_size 500M;
    client_body_timeout 600s;
}

Performance Optimization

# Enhanced configuration with caching and compression

upstream readur_backend {
    server localhost:8000 max_fails=3 fail_timeout=30s;
    server localhost:8001 backup;  # Optional backup server
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name readur.company.com;

    # ... SSL configuration ...

    # Compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1000;
    gzip_types text/plain text/css text/javascript 
               application/javascript application/json 
               application/xml image/svg+xml;

    # Static file caching
    location /static/ {
        alias /var/www/readur/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # Media files
    location /media/ {
        alias /var/www/readur/media/;
        expires 7d;
        add_header Cache-Control "public";

        # Security for uploaded files
        add_header Content-Disposition "attachment";
        add_header X-Content-Type-Options "nosniff";
    }

    # API endpoints
    location /api/ {
        proxy_pass http://readur_backend;

        # No caching for API
        proxy_no_cache 1;
        proxy_cache_bypass 1;

        # Rate limiting
        limit_req zone=api_limit burst=20 nodelay;
        limit_req_status 429;
    }

    # Main application
    location / {
        proxy_pass http://readur_backend;

        # ... proxy headers ...

        # Connection pooling
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

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

Apache Configuration

Basic Setup with mod_proxy

# /etc/apache2/sites-available/readur.conf
<VirtualHost *:80>
    ServerName readur.company.com

    # Redirect to HTTPS
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>

<VirtualHost *:443>
    ServerName readur.company.com

    # SSL Configuration
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/readur.crt
    SSLCertificateKeyFile /etc/ssl/private/readur.key
    SSLProtocol -all +TLSv1.2 +TLSv1.3
    SSLCipherSuite HIGH:!aNULL:!MD5:!3DES
    SSLHonorCipherOrder on

    # Security Headers
    Header always set Strict-Transport-Security "max-age=31536000"
    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

    ProxyPass / http://localhost:8000/
    ProxyPassReverse / http://localhost:8000/

    # WebSocket support
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/?(.*) "ws://localhost:8000/$1" [P,L]

    # Request headers
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"

    # Timeouts
    ProxyTimeout 600

    # File upload size
    LimitRequestBody 524288000
</VirtualHost>

Enable Required Modules

# Enable necessary Apache modules
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
sudo a2enmod headers
sudo a2enmod rewrite
sudo a2enmod ssl

# Enable site and reload
sudo a2ensite readur
sudo systemctl reload apache2

Caddy Configuration

Automatic HTTPS with Caddy

# /etc/caddy/Caddyfile
readur.company.com {
    # Automatic HTTPS with Let's Encrypt

    # Reverse proxy
    reverse_proxy localhost:8000 {
        # Headers
        header_up Host {host}
        header_up X-Real-IP {remote}
        header_up X-Forwarded-For {remote}
        header_up X-Forwarded-Proto {scheme}

        # Health check
        health_uri /health
        health_interval 30s
        health_timeout 5s

        # Load balancing (if multiple backends)
        # lb_policy round_robin
        # lb_try_duration 5s
    }

    # File upload size
    request_body {
        max_size 500MB
    }

    # Timeouts
    timeouts {
        read 5m
        write 5m
        idle 10m
    }

    # Compression
    encode gzip zstd

    # 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"
        Referrer-Policy "strict-origin-when-cross-origin"
        -Server
    }

    # Static files
    handle_path /static/* {
        root * /var/www/readur/static
        file_server
        header Cache-Control "public, max-age=2592000"
    }

    # Rate limiting
    rate_limit {
        zone api {
            key {remote_host}
            events 100
            window 1m
        }
    }

    # Logging
    log {
        output file /var/log/caddy/readur.log {
            roll_size 100mb
            roll_keep 5
        }
        format json
    }
}

Traefik Configuration

Docker-based Setup

# docker-compose.yml
version: '3.8'

services:
  traefik:
    image: traefik:v2.10
    command:
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "[email protected]"
      - "--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:
      - readur

  readur:
    image: readur:latest
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.readur.rule=Host(`readur.company.com`)"
      - "traefik.http.routers.readur.entrypoints=websecure"
      - "traefik.http.routers.readur.tls.certresolver=letsencrypt"
      - "traefik.http.services.readur.loadbalancer.server.port=8000"
      # Middleware
      - "traefik.http.middlewares.readur-headers.headers.stsSeconds=31536000"
      - "traefik.http.middlewares.readur-headers.headers.stsIncludeSubdomains=true"
      - "traefik.http.middlewares.readur-headers.headers.frameDeny=true"
      - "traefik.http.middlewares.readur-headers.headers.contentTypeNosniff=true"
      - "traefik.http.middlewares.readur-ratelimit.ratelimit.average=100"
      - "traefik.http.routers.readur.middlewares=readur-headers,readur-ratelimit"
    networks:
      - readur

networks:
  readur:
    external: true

HAProxy Configuration

Load Balancing Setup

# /etc/haproxy/haproxy.cfg
global
    maxconn 4096
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    user haproxy
    group haproxy
    daemon

    # SSL/TLS
    ssl-default-bind-ciphers ECDHE+AESGCM:ECDHE+AES256:!aNULL:!MD5:!DSS
    ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
    tune.ssl.default-dh-param 2048

defaults
    mode http
    log global
    option httplog
    option dontlognull
    option http-server-close
    option forwardfor except 127.0.0.0/8
    option redispatch
    retries 3
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

    # Compression
    compression algo gzip
    compression type text/html text/plain text/css text/javascript application/json

# Frontend
frontend readur_frontend
    bind *:80
    bind *:443 ssl crt /etc/ssl/readur.pem

    # Redirect HTTP to HTTPS
    redirect scheme https if !{ ssl_fc }

    # Security headers
    http-response set-header Strict-Transport-Security "max-age=31536000"
    http-response set-header X-Frame-Options "SAMEORIGIN"
    http-response set-header X-Content-Type-Options "nosniff"

    # ACLs
    acl is_websocket hdr(Upgrade) -i WebSocket
    acl is_api path_beg /api/
    acl is_static path_beg /static/

    # Rate limiting
    stick-table type ip size 100k expire 30s store http_req_rate(10s)
    http-request track-sc0 src
    http-request deny if { sc_http_req_rate(0) gt 100 }

    # Routing
    use_backend readur_websocket if is_websocket
    use_backend readur_static if is_static
    use_backend readur_api if is_api
    default_backend readur_app

# Backends
backend readur_app
    balance roundrobin
    option httpchk GET /health
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    server app1 localhost:8000 check
    server app2 localhost:8001 check backup

backend readur_api
    balance leastconn
    server app1 localhost:8000 check
    server app2 localhost:8001 check backup

backend readur_static
    server static localhost:8080 check

backend readur_websocket
    server ws1 localhost:8000 check

SSL/TLS Configuration

Let's Encrypt with Certbot

# Install Certbot
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx

# Obtain certificate
sudo certbot --nginx -d readur.company.com

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

# Cron job for renewal
echo "0 0,12 * * * root python -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew -q" | sudo tee -a /etc/crontab > /dev/null

Security Best Practices

# Strong SSL configuration
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;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/chain.pem;

# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

Performance Tuning

Connection Optimization

# NGINX connection tuning
events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    # Keepalive
    keepalive_timeout 65;
    keepalive_requests 100;

    # Buffers
    client_body_buffer_size 128k;
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;
    output_buffers 32 32k;
    postpone_output 1460;

    # File handling
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
}

Caching Strategy

# Cache configuration
proxy_cache_path /var/cache/nginx/readur 
    levels=1:2 
    keys_zone=readur_cache:100m 
    max_size=10g 
    inactive=60m
    use_temp_path=off;

location /api/documents/search {
    proxy_cache readur_cache;
    proxy_cache_valid 200 10m;
    proxy_cache_valid 404 1m;
    proxy_cache_key "$scheme$request_method$host$request_uri$args";
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_background_update on;
    proxy_cache_lock on;
    add_header X-Cache-Status $upstream_cache_status;
}

Monitoring

Access Logs

# Custom log format
log_format readur '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  '"$http_referer" "$http_user_agent" '
                  'rt=$request_time uct="$upstream_connect_time" '
                  'uht="$upstream_header_time" urt="$upstream_response_time"';

access_log /var/log/nginx/readur_access.log readur buffer=32k flush=5s;
error_log /var/log/nginx/readur_error.log warn;

Health Checks

# Health check endpoint
location /nginx-health {
    access_log off;
    add_header Content-Type text/plain;
    return 200 "healthy\n";
}

Troubleshooting

Common Issues

502 Bad Gateway

# Check if Readur is running
curl -I http://localhost:8000/health

# Check logs
tail -f /var/log/nginx/error.log
docker-compose logs readur

413 Request Entity Too Large

# Increase upload limits
client_max_body_size 1G;
client_body_buffer_size 10M;

Slow Response Times

# Check upstream response time
tail -f /var/log/nginx/readur_access.log | grep "urt="

# Enable upstream keepalive
upstream readur_backend {
    server localhost:8000;
    keepalive 32;
}