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