# CTAO portal demo — static serving. The cache split is what makes the # 10-second publish loop feel instant: HTML is revalidated on every request # (a symlink flip shows up on the next refresh), fingerprinted assets are # cached forever. server { listen 80; server_name _; root /srv/releases/current; charset utf-8; server_tokens off; error_page 404 /404.html; # Astro emits 404.html at the site root # Directory redirects (/admin -> /admin/) must stay relative: an absolute # redirect is built from listen port 80 and loses the real port whenever the # site is reached through a tunnel or a proxy on a non-default port. absolute_redirect off; # Security headers are REPEATED in every location on purpose: nginx # `add_header` inheritance is all-or-nothing — any add_header in a location # discards ALL server-level ones, so server-level headers would silently # vanish. `always` keeps them on error responses (404) too. # Fingerprinted build assets (/_astro/..*) — immutable location /_astro/ { add_header Cache-Control "public, max-age=31536000, immutable" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header X-Frame-Options "SAMEORIGIN" always; } # Editor-uploaded media (stable paths, may be re-uploaded) — short cache location /uploads/ { add_header Cache-Control "public, max-age=3600" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header X-Frame-Options "SAMEORIGIN" always; } # Everything else: HTML pages, feeds, /admin (Sveltia is static files too) location / { try_files $uri $uri/ =404; add_header Cache-Control "no-cache" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header X-Frame-Options "SAMEORIGIN" always; } gzip on; gzip_types text/css application/javascript application/json image/svg+xml application/rss+xml text/xml application/xml; }