Nginx as a Secure Reverse Proxy
Nginx as a Secure Reverse Proxy
Configure Nginx as a security-focused reverse proxy:
# /etc/nginx/sites-available/secure-reverse-proxy
upstream backend_servers {
# Define backend servers with health checks
server backend1.internal:8080 max_fails=3 fail_timeout=30s;
server backend2.internal:8080 max_fails=3 fail_timeout=30s;
server backend3.internal:8080 max_fails=3 fail_timeout=30s backup;
# Connection settings
keepalive 32;
keepalive_requests 100;
keepalive_timeout 60s;
}
# Rate limiting zones
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/m;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_conn_zone $binary_remote_addr zone=addr:10m;
# GeoIP blocking (optional)
# geoip_country /usr/share/GeoIP/GeoIP.dat;
# map $geoip_country_code $allowed_country {
# default yes;
# CN no;
# RU no;
# }
# Security headers map
map $upstream_http_content_type $security_headers {
default "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
~*text/html "default-src 'self'; script-src 'self' 'nonce-$request_id'; style-src 'self' 'nonce-$request_id'; object-src 'none'; base-uri 'self';";
}
server {
listen 443 ssl http2;
server_name example.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
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;
# Security settings
client_max_body_size 10M;
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
# Timeouts
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 10s;
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
# Hide backend details
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
more_clear_headers Server;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" 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;
add_header Content-Security-Policy $security_headers always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Main location block
location / {
# Rate limiting
limit_req zone=general burst=20 nodelay;
limit_conn addr 10;
# Country blocking (if GeoIP enabled)
# if ($allowed_country = no) {
# return 403;
# }
# Security checks
if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS)$) {
return 405;
}
# Block suspicious user agents
if ($http_user_agent ~* (bot|crawl|spider|scraper|scan)) {
return 403;
}
# Proxy settings
proxy_pass http://backend_servers;
proxy_http_version 1.1;
# Headers to backend
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-Request-ID $request_id;
# Connection reuse
proxy_set_header Connection "";
# Buffering settings
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
# Cache settings (if applicable)
proxy_cache_bypass $http_upgrade;
proxy_no_cache $http_pragma $http_authorization;
}
# API endpoint with stricter limits
location /api/ {
limit_req zone=api burst=10 nodelay;
limit_conn addr 5;
# Additional API security
if ($http_content_type !~* "application/json") {
return 415;
}
proxy_pass http://backend_servers;
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-API-Key $http_x_api_key;
# API-specific timeouts
proxy_read_timeout 30s;
}
# Login endpoint with strict rate limiting
location /login {
limit_req zone=login burst=5 nodelay;
limit_conn addr 2;
# Additional login security
if ($request_method != POST) {
return 405;
}
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Health check endpoint (no rate limiting)
location /health {
access_log off;
proxy_pass http://backend_servers;
proxy_set_header Host $host;
}
# Block access to sensitive paths
location ~ /\.(git|svn|env|config|htaccess) {
deny all;
return 404;
}
# Custom error pages
error_page 403 /errors/403.html;
error_page 404 /errors/404.html;
error_page 500 502 503 504 /errors/50x.html;
location ^~ /errors/ {
internal;
root /usr/share/nginx/html;
}
}