s1ne/nginx.conf
2026-09-09 06:28:04 +00:00

277 lines
8.2 KiB
Nginx Configuration File

# ./nginx.conf
# Mount as: ./nginx.conf:/etc/nginx/nginx.conf:ro
#
# Request path:
# Internet -> Caddy (TLS) -> Nginx -> FastAPI service "s1ne:8003"
#
# Nginx directly serves ./static mounted at /usr/share/nginx/html.
# FastAPI handles /api/, /media/, /share/, /healthz, and /readyz.
#
# A separately mounted protected directory is exposed at /files/.
# Example container mount: ./protected:/protected:ro
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;
client_max_body_size 256k;
client_header_timeout 15s;
client_body_timeout 15s;
send_timeout 60s;
server_tokens off;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
image/svg+xml;
# Caddy is the only trusted upstream proxy.
# Must match TRUSTED_PROXY_CIDRS used by Uvicorn/FastAPI.
set_real_ip_from 172.30.20.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# Limits by real client IP.
limit_req_zone $binary_remote_addr zone=general_limit:10m rate=20r/s;
limit_req_zone $binary_remote_addr zone=search_limit:10m rate=2r/s;
limit_req_zone $binary_remote_addr zone=audio_limit:10m rate=3r/s;
limit_conn_zone $binary_remote_addr zone=per_ip_conn:10m;
upstream fastapi {
server s1ne:8003;
keepalive 32;
}
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
autoindex off;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
limit_conn per_ip_conn 20;
# Block accidental secret/source exposure from the public static mount.
location ~ /\.(?!well-known) {
deny all;
access_log off;
log_not_found off;
}
location ~* \.(?:bak|conf|dist|fla|in[ci]|log|orig|psd|sh|sql|swp|tmp)$ {
deny all;
access_log off;
log_not_found off;
}
# Public SPA entry page.
location = / {
try_files /index.html =404;
add_header Cache-Control "no-cache" always;
}
# Public static assets.
# /static/app.js -> /usr/share/nginx/html/app.js
location ^~ /static/ {
alias /usr/share/nginx/html/;
access_log off;
expires 1h;
add_header Cache-Control "public, max-age=3600" always;
}
# Long-cache versioned JavaScript asset.
location = /static/app--3.js {
alias /usr/share/nginx/html/app--3.js;
access_log off;
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable" always;
}
# Optional public robots file.
location = /robots.txt {
try_files /robots.txt =404;
default_type text/plain;
add_header Cache-Control "public, max-age=86400" always;
}
# Protected files from a separate mounted directory.
#
# URL: /files/report.pdf
# Container: /protected/report.pdf
location = /files {
return 301 /files/;
}
location ^~ /files/ {
auth_basic "Private media";
auth_basic_user_file /etc/nginx/.htpasswd;
alias /protected/;
autoindex off;
}
# FastAPI validates share-token syntax and returns SPA documents.
location ^~ /share/ {
proxy_pass http://fastapi;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
# Expensive yt-dlp endpoints.
location ~ ^/api/(search|recommendations)$ {
limit_req zone=search_limit burst=15 nodelay;
limit_req_status 429;
proxy_pass http://fastapi;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_connect_timeout 10s;
proxy_read_timeout 120s;
proxy_send_timeout 60s;
}
# Audio streaming, preserving Range/seek support.
location ^~ /media/ {
limit_req zone=audio_limit burst=30 nodelay;
limit_req_status 429;
proxy_pass http://fastapi;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_buffering off;
proxy_request_buffering off;
proxy_connect_timeout 10s;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
send_timeout 3600s;
}
# Audio download streaming.
location ^~ /api/download/ {
limit_req zone=audio_limit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://fastapi;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_buffering off;
proxy_request_buffering off;
proxy_connect_timeout 10s;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
send_timeout 3600s;
}
# FastAPI health endpoints.
location = /healthz {
access_log off;
proxy_pass http://fastapi/healthz;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_connect_timeout 5s;
proxy_read_timeout 15s;
}
location = /readyz {
access_log off;
proxy_pass http://fastapi/readyz;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_connect_timeout 5s;
proxy_read_timeout 15s;
}
# Remaining FastAPI endpoints.
location /api/ {
limit_req zone=general_limit burst=60 nodelay;
limit_req_status 429;
proxy_pass http://fastapi;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_connect_timeout 10s;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
# Default deny: only explicitly configured paths are exposed.
location / {
return 404;
}
}
}