# WebSocket settings map $http_upgrade $connection_upgrade { default upgrade; '' close; } # Cache configuration proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=24h max_size=1g; # API cache for frequently requested endpoints proxy_cache_path /var/cache/nginx/api levels=1:2 keys_zone=API_CACHE:10m inactive=60m max_size=100m; server { listen 80 default_server; # Large cookie support large_client_header_buffers 4 32k; proxy_buffer_size 32k; proxy_busy_buffers_size 48k; proxy_buffers 4 32k; # Logging access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location ^~ /assets/ { proxy_pass http://127.0.0.1:3000; proxy_cache STATIC; proxy_cache_valid 200 302 24h; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; add_header Cache-Control "public, max-age=31536000, immutable"; 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 ^~ /_assets/ { proxy_pass http://127.0.0.1:3000; proxy_cache STATIC; proxy_cache_valid 200 302 24h; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; add_header Cache-Control "public, max-age=31536000, immutable"; 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 ~* ^/w(\d+)(/(?:assets|_assets)/.*)$ { set $worker $1; proxy_pass http://127.0.0.1:$worker_port$2$is_args$args; proxy_cache STATIC; proxy_cache_valid 200 302 24h; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; add_header Cache-Control "public, max-age=31536000, immutable"; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $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; } # Worker locations - Processing this first so worker-specific requests are handled by workers # This prevents static file regexes from capturing worker requests location ~* ^/w(\d+)(/.*)?$ { set $worker $1; # Preserve query string by appending $is_args$args proxy_pass http://127.0.0.1:$worker_port$2$is_args$args; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $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; } # Randomly distribute new-game creation across the live workers; the worker # mints a self-owned id and returns it. The openfront_workers upstream is # generated at container start from NUM_WORKERS (generate-nginx-upstream.sh) # and can be reused by any future endpoint that wants the same balancing. location = /api/create_game { proxy_pass http://openfront_workers; proxy_http_version 1.1; 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; } # Admin bot create-game: same random-worker balancing as /api/create_game. # The worker checks the admin bot key, mints a self-owned id, and returns it. location = /api/adminbot/create_game { proxy_pass http://openfront_workers; proxy_http_version 1.1; 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; } # Static file handling with proper MIME types and consistent caching location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|woff|woff2|ttf|eot)$ { proxy_pass http://127.0.0.1:3000; # Include MIME types include /etc/nginx/mime.types; # Cache configuration for static files proxy_cache STATIC; proxy_cache_valid 200 302 24h; # Cache successful responses for 24 hours proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; # Show cache status in response headers add_header X-Cache-Status $upstream_cache_status; # Standard proxy 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; # Default cache policy for static files add_header Cache-Control "public, max-age=86400"; # 24 hours } # /api/health endpoint - No caching, always hit the backend location = /api/health { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; # Cache configuration - No caching for health checks proxy_cache off; add_header X-Cache-Status "BYPASS"; add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate"; add_header Pragma "no-cache"; add_header Expires "0"; # Standard proxy 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; } # /commit.txt endpoint - Cache for 5 seconds location = /commit.txt { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; # Cache configuration proxy_cache API_CACHE; proxy_cache_valid 200 5s; # Cache successful responses for 5 seconds proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; # Standard proxy 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; } # Binary files caching location ~* \.(bin|dat|exe|dll|so|dylib)$ { proxy_pass http://127.0.0.1:3000; add_header Cache-Control "public, max-age=31536000, immutable"; # 1 year for binary files proxy_cache STATIC; proxy_cache_valid 200 302 24h; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; 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; } # Specific file type caching rules (outside the /static/ location) location ~* \.js$ { proxy_pass http://127.0.0.1:3000; add_header Content-Type application/javascript; add_header Cache-Control "public, max-age=31536000, immutable"; # 1 year for JS files proxy_cache STATIC; proxy_cache_valid 200 302 24h; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; 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 ~* \.css$ { proxy_pass http://127.0.0.1:3000; add_header Content-Type text/css; add_header Cache-Control "public, max-age=31536000, immutable"; # 1 year for CSS files proxy_cache STATIC; proxy_cache_valid 200 302 24h; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; 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; } # Updated HTML file caching - 1 second cache location ~* \.html$ { proxy_pass http://127.0.0.1:3000; add_header Content-Type text/html; add_header Cache-Control "public, max-age=1"; # 1 second for HTML files proxy_cache STATIC; proxy_cache_valid 200 302 1s; # Cache successful responses for 1 second proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; 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; } # Root location with short shared cache for the app shell location = / { proxy_pass http://127.0.0.1:3000; # Cache the shared app shell briefly at the proxy; browser/CDN policy # comes from the upstream Cache-Control header. proxy_cache STATIC; proxy_cache_valid 200 302 300s; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; # Show cache status in response headers add_header X-Cache-Status $upstream_cache_status; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $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; } # Default location for all other requests location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $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; } }