- Frontend on nginx-unprivileged (non-root, container port 8080) - Replace unmaintained passlib with bcrypt 5.0.0 (hash-compatible) - Pass DOCS_ENABLED through to the backend container - Raise db mem_limit to 768m for MySQL 8.4 Co-Authored-By: Claude Opus 5.5 <[email protected]>
99 lines
3.6 KiB
Nginx Configuration File
99 lines
3.6 KiB
Nginx Configuration File
# Real client IP — traffic arrives via Cloudflare → NPM (172.16.22.21), so
|
|
# $remote_addr would otherwise be NPM for every request and rate limits would
|
|
# be shared by all clients. Walk X-Forwarded-For right-to-left past trusted hops.
|
|
# Cloudflare ranges: https://www.cloudflare.com/ips/
|
|
set_real_ip_from 172.16.22.21;
|
|
set_real_ip_from 173.245.48.0/20;
|
|
set_real_ip_from 103.21.244.0/22;
|
|
set_real_ip_from 103.22.200.0/22;
|
|
set_real_ip_from 103.31.4.0/22;
|
|
set_real_ip_from 141.101.64.0/18;
|
|
set_real_ip_from 108.162.192.0/18;
|
|
set_real_ip_from 190.93.240.0/20;
|
|
set_real_ip_from 188.114.96.0/20;
|
|
set_real_ip_from 197.234.240.0/22;
|
|
set_real_ip_from 198.41.128.0/17;
|
|
set_real_ip_from 162.158.0.0/15;
|
|
set_real_ip_from 104.16.0.0/13;
|
|
set_real_ip_from 104.24.0.0/14;
|
|
set_real_ip_from 172.64.0.0/13;
|
|
set_real_ip_from 131.0.72.0/22;
|
|
set_real_ip_from 2400:cb00::/32;
|
|
set_real_ip_from 2606:4700::/32;
|
|
set_real_ip_from 2803:f800::/32;
|
|
set_real_ip_from 2405:b500::/32;
|
|
set_real_ip_from 2405:8100::/32;
|
|
set_real_ip_from 2a06:98c0::/29;
|
|
set_real_ip_from 2c0f:f248::/32;
|
|
real_ip_header X-Forwarded-For;
|
|
real_ip_recursive on;
|
|
|
|
# Rate limiting zones — included inside http{} block
|
|
limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=5r/m;
|
|
limit_req_zone $binary_remote_addr zone=tv_limit:10m rate=10r/m;
|
|
|
|
server {
|
|
listen 8080;
|
|
server_tokens off;
|
|
root /usr/share/nginx/html;
|
|
index index.html;
|
|
|
|
# Security headers
|
|
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 "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' ws: wss:; font-src 'self' data:;" always;
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
|
|
# Gzip compression
|
|
gzip on;
|
|
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml;
|
|
gzip_min_length 1024;
|
|
|
|
# Rate-limited auth endpoints (checked before the generic /api/ block)
|
|
location ~ ^/api/(auth/(login|register)|admin/login)$ {
|
|
limit_req zone=auth_limit burst=3 nodelay;
|
|
limit_req_status 429;
|
|
proxy_pass http://backend:8000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
}
|
|
|
|
# Rate-limited TV dashboard endpoint (public, token-based)
|
|
location ~ ^/api/dashboard/ {
|
|
limit_req zone=tv_limit burst=5 nodelay;
|
|
limit_req_status 429;
|
|
proxy_pass http://backend:8000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
}
|
|
|
|
# API proxy → FastAPI backend
|
|
location /api/ {
|
|
proxy_pass http://backend:8000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
}
|
|
|
|
# WebSocket proxy → FastAPI backend
|
|
location /ws/ {
|
|
limit_req zone=tv_limit burst=5 nodelay;
|
|
limit_req_status 429;
|
|
proxy_pass http://backend:8000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host $host;
|
|
proxy_read_timeout 3600s;
|
|
}
|
|
|
|
# Vue Router — all other paths serve index.html (SPA fallback)
|
|
location / {
|
|
try_files $uri $uri/ /index.html;
|
|
}
|
|
}
|