diff --git a/nginx/popup-proxy.conf b/nginx/popup-proxy.conf index d9b3e06..7aa6e95 100644 --- a/nginx/popup-proxy.conf +++ b/nginx/popup-proxy.conf @@ -43,6 +43,18 @@ location /proxy/arxiv/ { proxy_set_header User-Agent "levineuwirth.org popup-proxy (ln@levineuwirth.org)"; proxy_ssl_server_name on; + # Keep the security baseline: the add_header directives below + # would otherwise drop it for /proxy/ responses (same pattern + # as archive.conf). The upstream's own security headers are hidden + # first — browsers honor only the FIRST Strict-Transport-Security + # header (RFC 6797 §8.1), so an upstream's short max-age passing + # through ahead of ours would downgrade the domain's cached HSTS + # policy on every popup fetch. + proxy_hide_header Strict-Transport-Security; + proxy_hide_header Content-Security-Policy; + proxy_hide_header X-Frame-Options; + include snippets/security-headers.conf; + proxy_cache popup_proxy; proxy_cache_valid 200 30d; proxy_cache_valid any 5m; @@ -68,6 +80,18 @@ location /proxy/archive/ { proxy_set_header User-Agent "levineuwirth.org popup-proxy (ln@levineuwirth.org)"; proxy_ssl_server_name on; + # Keep the security baseline: the add_header directives below + # would otherwise drop it for /proxy/ responses (same pattern + # as archive.conf). The upstream's own security headers are hidden + # first — browsers honor only the FIRST Strict-Transport-Security + # header (RFC 6797 §8.1), so an upstream's short max-age passing + # through ahead of ours would downgrade the domain's cached HSTS + # policy on every popup fetch. + proxy_hide_header Strict-Transport-Security; + proxy_hide_header Content-Security-Policy; + proxy_hide_header X-Frame-Options; + include snippets/security-headers.conf; + proxy_cache popup_proxy; proxy_cache_valid 200 7d; proxy_cache_valid any 5m; @@ -96,6 +120,18 @@ location /proxy/pubmed/ { # caching this is rarely exercised, but the burst guards a hot page. limit_req zone=pubmed burst=3 nodelay; + # Keep the security baseline: the add_header directives below + # would otherwise drop it for /proxy/ responses (same pattern + # as archive.conf). The upstream's own security headers are hidden + # first — browsers honor only the FIRST Strict-Transport-Security + # header (RFC 6797 §8.1), so an upstream's short max-age passing + # through ahead of ours would downgrade the domain's cached HSTS + # policy on every popup fetch. + proxy_hide_header Strict-Transport-Security; + proxy_hide_header Content-Security-Policy; + proxy_hide_header X-Frame-Options; + include snippets/security-headers.conf; + proxy_cache popup_proxy; proxy_cache_valid 200 30d; proxy_cache_valid any 5m; diff --git a/nginx/static-assets.conf b/nginx/static-assets.conf index a864b2f..1ed4d34 100644 --- a/nginx/static-assets.conf +++ b/nginx/static-assets.conf @@ -55,16 +55,26 @@ brotli off; # we ship pre-compressed sidecars only, no on-the-fly b # instantaneous. Same reasoning applies to fingerprinted fonts and the # locally vendored ML model files. location ^~ /pdfjs/ { + # Re-include the security baseline: this location declares its + # own add_header, which (per nginx inheritance rules) would + # otherwise drop HSTS/nosniff/CSP for everything it serves — + # and nosniff matters most on exactly these JS/CSS responses. + # Same pattern as archive.conf. + include snippets/security-headers.conf; add_header Cache-Control "public, max-age=31536000, immutable" always; access_log off; } location ^~ /fonts/ { + # Keep the security baseline (see first location above). + include snippets/security-headers.conf; add_header Cache-Control "public, max-age=31536000, immutable" always; access_log off; } location ^~ /models/ { + # Keep the security baseline (see first location above). + include snippets/security-headers.conf; add_header Cache-Control "public, max-age=31536000, immutable" always; access_log off; } @@ -74,6 +84,8 @@ location ^~ /models/ { # keeps them responsive to deploys (~1h staleness window for warm clients) # without forcing a fetch on every page navigation. location ~* \.(?:css|js|mjs|woff2?|svg|webp|png|jpg|jpeg|ico)$ { + # Keep the security baseline (see first location above). + include snippets/security-headers.conf; add_header Cache-Control "public, max-age=3600, must-revalidate" always; access_log off; }