# security-headers.conf — security baseline for the levineuwirth.org vhost. # # Place at /etc/nginx/snippets/security-headers.conf and `include` it # inside the server { } block of the vhost, alongside the other # snippets shipped from this repo: # # server { # server_name levineuwirth.org; # root /var/www/levineuwirth.org; # ... # include snippets/security-headers.conf; # include snippets/static-assets.conf; # include snippets/popup-proxy.conf; # } # Hide the nginx version from error pages and the Server header. server_tokens off; # HSTS — one year, with subdomains, preload-eligible. Only safe to # enable once HTTPS is the only listener (the :80 vhost should already # 301 → https). Once preload is requested via hstspreload.org, the # max-age cannot be lowered without removing from the list manually. add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # Block MIME-sniffing — required for safe text/plain and svg responses. add_header X-Content-Type-Options "nosniff" always; # Defense against clickjacking. Modern browsers honor CSP frame-ancestors # (set below); X-Frame-Options is kept for legacy clients. add_header X-Frame-Options "DENY" always; # Strip the Referer header to "scheme + host" on cross-origin requests. add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Default-deny powerful APIs. interest-cohort opts out of FLoC/Topics. add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always; # Content Security Policy. Shipped in Report-Only mode initially — # promote to enforcing (strip the "-Report-Only" suffix) once the # report stream has been clean for a week. # # External origins justified inline: # cdn.jsdelivr.net KaTeX CSS + JS, Vega / Vega-Lite / Vega-Embed # *.basemaps.cartocdn.com Leaflet basemap tiles (photography map only) # # Why 'unsafe-inline' on style: # - photography.html emits for # palette swatches (data-driven, can't be hashed). # - KaTeX adds inline styles when rendering math at runtime. # # Why 'unsafe-eval' on script: # - vega-embed compiles Vega-Lite specs at runtime via new Function(). # Removing this would require pre-compiling specs at build time. # # To collect violation reports, set up a `report-uri` endpoint and add # `report-uri /csp-report;` (and/or `report-to ;`) below. add_header Content-Security-Policy-Report-Only "default-src 'self'; \ script-src 'self' 'unsafe-eval' https://cdn.jsdelivr.net; \ style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; \ img-src 'self' data: https://*.basemaps.cartocdn.com; \ font-src 'self' data:; \ connect-src 'self'; \ frame-ancestors 'none'; \ base-uri 'self'; \ form-action 'self'; \ object-src 'none'; \ upgrade-insecure-requests" always;