Files
flux-srl/nginx/conf.d/flux.conf
T
davidherran 1a4abfc7f2
Deploy to VPS / deploy (push) Has been cancelled
nginx: include lethepowerflux.com → rf-flux.com 301 redirect
The VPS already had a server block redirecting lethepowerflux.com and
www.lethepowerflux.com to https://www.rf-flux.com, but it lived only on
the live config — not in git. That's why the latest pull complained
about local changes that would be overwritten.

Adding it here so the repo is the single source of truth for the Nginx
config again. Behaviour is unchanged on the VPS (redirect was already
in place) — this commit just lets future git pulls flow without
manual intervention.
2026-05-05 13:17:34 -05:00

223 lines
8.2 KiB
Plaintext

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/s;
upstream nextjs {
server app:3000;
keepalive 32;
}
# Legacy domain redirect — anyone landing on lethepowerflux.com lands on
# the canonical https://www.rf-flux.com host instead. SEO-safe 301.
server {
listen 80;
server_name lethepowerflux.com www.lethepowerflux.com;
return 301 https://www.rf-flux.com$request_uri;
}
server {
listen 80;
server_name rf-flux.com www.rf-flux.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
http2 on;
client_max_body_size 500M;
server_name rf-flux.com www.rf-flux.com;
ssl_certificate /etc/letsencrypt/live/rf-flux.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rf-flux.com/privkey.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;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Next.js bundles use content hashing — safe to cache forever
location /_next/static/ {
proxy_pass http://nextjs;
expires 365d;
add_header Cache-Control "public, immutable";
access_log off;
}
# Next.js image optimizer — short cache, browser revalidates
location /_next/image {
proxy_pass http://nextjs;
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_cache_bypass $http_upgrade;
add_header Cache-Control "public, max-age=300, must-revalidate" always;
}
location /hq-command/login {
limit_req zone=login burst=10 nodelay;
proxy_pass http://nextjs;
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;
}
# Asset uploads (large files, long timeout)
location /api/assets {
client_max_body_size 500M;
proxy_pass http://nextjs;
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_read_timeout 600s;
proxy_send_timeout 600s;
proxy_request_buffering off;
}
location /api/public-upload {
client_max_body_size 500M;
proxy_pass http://nextjs;
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_read_timeout 600s;
proxy_send_timeout 600s;
proxy_request_buffering off;
}
location /hq-command/ {
proxy_pass http://nextjs;
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 /api/chat {
limit_req zone=api burst=20 nodelay;
proxy_pass http://nextjs;
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_http_version 1.1;
proxy_set_header Connection '';
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 120s;
}
location /api/health {
proxy_pass http://nextjs;
access_log off;
}
# ─────────────────────────────────────────────────────────────────
# User-uploaded assets — served directly from disk (bypass Next.js)
#
# Cache strategy: short max-age + must-revalidate.
# Browser caches for 5 minutes, then asks Nginx "did this change?"
# via If-Modified-Since. Nginx auto-replies 304 (Not Modified) if the
# file's mtime is unchanged, or serves the new file if it changed.
# This means new CMS uploads appear within ~5 min without rebuild
# AND saved bandwidth on unchanged files.
# ─────────────────────────────────────────────────────────────────
location /cases/ {
alias /srv/cases/;
add_header Cache-Control "public, max-age=300, must-revalidate" always;
access_log off;
}
location /applications/ {
alias /srv/applications/;
add_header Cache-Control "public, max-age=300, must-revalidate" always;
access_log off;
}
location /news/ {
alias /srv/news/;
add_header Cache-Control "public, max-age=300, must-revalidate" always;
access_log off;
}
location /parts/ {
alias /srv/parts/;
add_header Cache-Control "public, max-age=300, must-revalidate" always;
access_log off;
}
location /operations-inbox/ {
alias /srv/operations-inbox/;
add_header Cache-Control "private, max-age=60, must-revalidate" always;
access_log off;
}
location /footage/ {
alias /srv/footage/;
add_header Cache-Control "public, max-age=300, must-revalidate" always;
access_log off;
}
location /branding/ {
alias /srv/branding/;
add_header Cache-Control "public, max-age=300, must-revalidate" always;
access_log off;
}
location / {
proxy_pass http://nextjs;
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;
# Public-facing host + port so Next.js builds correct absolute
# redirect URLs (without leaking the internal container port 3000).
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Strip any leaked container port from upstream redirects, just in
# case Next.js still builds Location headers with :3000.
proxy_redirect ~^https?://[^/:]+:3000(/.*)$ https://$host$1;
# ── Shared HTML cache ───────────────────────────────────────────
# Caches GET responses that come back with a Cache-Control header
# from Next.js (the middleware sets s-maxage=60 on public marketing
# pages). Authenticated requests skip the cache entirely. While a
# cached entry is being refreshed, other visitors keep getting the
# stale copy — no thundering herd, no cold starts visible to users.
proxy_cache flux_html;
proxy_cache_revalidate on;
proxy_cache_min_uses 1;
proxy_cache_lock on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_methods GET HEAD;
# Bypass cache for authenticated sessions (admin CMS or B2B portal)
# so logged-in users always see fresh, per-account content.
proxy_cache_bypass $cookie_flux_session $cookie_flux_b2b_session $http_pragma;
proxy_no_cache $cookie_flux_session $cookie_flux_b2b_session $http_pragma;
# Surface cache status in response headers for debugging.
# X-Cache-Status: HIT | MISS | EXPIRED | STALE | UPDATING | BYPASS
add_header X-Cache-Status $upstream_cache_status always;
}
}