ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/crawler-signal-reference / framework-http-security-headers.md · Lines 1501–2397

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

A continuation of Janady13/crawler-signal-reference / framework-http-security-headers.md. The complete source identity, every recorded occurrence, and linked equations are on its first page.

Source transcript · continued

Download complete source text ↓2,397 lines · 104.7 KB
For sites that need to open cross origin popups (OAuth flows, payment processors): ```nginx add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always; ``` The popup can still communicate back via `window.opener.postMessage`, which is what OAuth flows need. ### 13.4 How To Verify ```bash # 1. Confirm header curl -sI https://example.com/ | grep -i cross-origin-opener-policy # 2. Test in browser console: # Open a popup: window.open("https://example.com", "_blank") # In the popup: window.opener # Expected: null when COOP is same-origin # 3. Verify cross origin isolation status (combined with COEP): # window.crossOriginIsolated # Expected: true when COOP same-origin AND COEP require-corp/credentialless ``` ### 13.5 Troubleshooting **Symptom: OAuth popup completes auth but cannot communicate back.** COOP is `same-origin`. The popup cannot reach `window.opener`. Fix: change to `same-origin-allow-popups`. **Symptom: window.crossOriginIsolated is false despite COEP plus COOP.** Possible causes: 1. COOP is `unsafe-none` or `same-origin-allow-popups` instead of `same-origin`. 2. The document was opened by a cross origin window (COOP cannot retroactively isolate). 3. An iframe in the document is cross origin without proper headers. **Symptom: window.opener is null in a popup that used to work.** COOP enforcement is in place. The popup must be from same origin or use postMessage with explicit origin checks. ### 13.6 How To Fix Common Breakage **Case: OAuth or social login broken after COOP enforcement.** Use `same-origin-allow-popups`: ```nginx add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always; ``` The OAuth popup can communicate back via postMessage. **Case: Need full cross origin isolation but OAuth must work.** Move OAuth flow to a separate domain that does not have strict COOP, then transfer the auth result via secure cookie back to the isolated domain. --- ## 14. THE CROSS ORIGIN ISOLATION PATTERN (COEP PLUS COOP PLUS CORP) ### 14.1 What It Achieves Cross origin isolation unlocks browser APIs that were disabled after Spectre: * `SharedArrayBuffer`: shared memory between threads. * `performance.now()` with high precision (nanoseconds instead of millisecond resolution). * `performance.measureUserAgentSpecificMemory()`. * WebAssembly threading. These features are required for sophisticated WebAssembly applications, scientific computing, video editing, and gaming in the browser. ### 14.2 The Three Header Requirement To achieve cross origin isolation, all three must be true: 1. **COOP: same-origin** on the document. 2. **COEP: require-corp** OR **COEP: credentialless** on the document. 3. **Every cross origin resource has CORP or CORS** allowing this document. Verify with `window.crossOriginIsolated` in the console. ### 14.3 Full Configuration ```nginx server { listen 443 ssl; server_name webapp.example.com; # ===== Cross origin isolation ===== add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Embedder-Policy "credentialless" always; # ===== CORP on all served resources ===== location ~* \.(jpg|jpeg|png|webp|svg|woff2|css|js|wasm)$ { add_header Cross-Origin-Resource-Policy "same-origin" always; # ... caching etc ... } # ===== Permissions-Policy must NOT block cross-origin-isolated ===== # Default is allow; just ensure we do not block it # add_header Permissions-Policy "cross-origin-isolated=(self), camera=(), ..." always; } ``` ### 14.4 How To Verify Cross Origin Isolation ```bash # 1. Confirm all three headers curl -sI https://webapp.example.com/ | grep -iE "cross-origin-(opener|embedder|resource)-policy" # 2. In browser DevTools console on the document: # window.crossOriginIsolated # Expected: true # 3. Verify SharedArrayBuffer is available: # typeof SharedArrayBuffer # Expected: "function" # 4. Verify high precision performance.now: # performance.now() # Expected: many decimal places of precision (not capped at millisecond resolution) ``` ### 14.5 Rollout Strategy Cross origin isolation is invasive. Rolling it out site wide breaks anything that loads cross origin resources without CORP. **Recommended approach:** 1. Scope cross origin isolation to a specific subdomain (e.g. `webapp.example.com`) rather than the whole site. 2. Self host every resource that subdomain needs. 3. Verify in DevTools that no resources are blocked. 4. Confirm `window.crossOriginIsolated === true`. 5. Only then enable the isolated APIs in code. Most Bubbles client sites do not need cross origin isolation and should not enable COEP/COOP. Apply only to sites that explicitly require SharedArrayBuffer or related APIs. --- ## 15. HOW THESE HEADERS INTERACT The nine headers form a layered system. Several specific interactions matter. ### 15.1 X-Frame-Options vs CSP frame-ancestors Both control framing. Modern browsers honor frame-ancestors when both are present and conflict. Older browsers honor X-Frame-Options. Best practice: set both with consistent values. ### 15.2 CSP and Permissions-Policy CSP controls resource loading. Permissions-Policy controls capability access. They are orthogonal. A page can have a permissive CSP but restrictive Permissions-Policy (or vice versa). Set both to maximize defense. ### 15.3 HSTS and CSP upgrade-insecure-requests HSTS upgrades the connection. `upgrade-insecure-requests` (CSP) upgrades subresource references in the HTML. They work together: HSTS upgrades the top level request, CSP upgrade-insecure-requests upgrades references within the page. ### 15.4 CORP, COEP, and COOP These three work together to provide cross origin isolation. None of them alone is sufficient. See Section 14 for the combined pattern. ### 15.5 Referrer-Policy and CSP report-uri When CSP sends a violation report, the report includes the URL where the violation occurred (document-uri). If Referrer-Policy is `no-referrer`, the destination of the report does not see a Referer header, but the report body itself still contains URLs. The two are independent. ### 15.6 X-Content-Type-Options and CSP nosniff is an absolute. CSP can layer additional restrictions but nosniff is non negotiable. Always set nosniff. ### 15.7 Permissions-Policy and Cross-Origin-Embedder-Policy The `cross-origin-isolated` directive in Permissions-Policy can block isolation even when COOP and COEP are set. If you want isolation, do not block it: `Permissions-Policy: cross-origin-isolated=(self)` or just omit the directive. --- ## 16. ASSET CLASS AND USE CASE RECIPES Paste ready blocks per use case. ### 16.1 Standard Bubbles client site (production, public marketing site) ```nginx # /etc/nginx/snippets/common-security-headers.conf add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), bluetooth=(), browsing-topics=(), interest-cohort=()" always; add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Resource-Policy "same-site" always; ``` CSP is per site (set separately, not in the shared snippet, because each site's CSP is unique). ### 16.2 Standard CSP for a marketing site with GTM, GA4, AdSense ```nginx add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com https://www.googleadservices.com https://googleads.g.doubleclick.net https://pagead2.googlesyndication.com https://*.googlesyndication.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob: https:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.doubleclick.net; frame-src 'self' https://www.googletagmanager.com https://www.google.com https://googleads.g.doubleclick.net; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; upgrade-insecure-requests" always; ``` ### 16.3 Strict CSP with nonce for FastAPI sidecar dynamic pages Generated per request in the upstream. nginx passes through: ```nginx location /dashboard { proxy_pass http://127.0.0.1:9090; # Sidecar emits CSP with nonce; nginx does not override } ``` See Section 6.4 for the FastAPI implementation. ### 16.4 Staging or development environment ```nginx server { server_name staging.example.com; include snippets/common-security-headers.conf; # Block all indexing add_header X-Robots-Tag "noindex, nofollow" always; # Permissive CSP for debugging add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' https:; report-uri /csp-report" always; # Basic auth as additional layer auth_basic "Staging"; auth_basic_user_file /etc/nginx/.staging-htpasswd; } ``` ### 16.5 OAuth or social login callback page Needs `same-origin-allow-popups` to communicate back from the OAuth popup: ```nginx location ~ ^/auth/(login|callback) { include snippets/common-security-headers-no-coop.conf; add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always; } ``` ### 16.6 WebAssembly application requiring cross origin isolation ```nginx server { server_name webapp.example.com; include snippets/common-security-headers-base.conf; add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Embedder-Policy "credentialless" always; location / { add_header Cross-Origin-Resource-Policy "same-origin" always; try_files $uri $uri/ /index.html; } } ``` ### 16.7 Public embeddable resources (logos, badges) ```nginx location /embed/ { add_header Cross-Origin-Resource-Policy "cross-origin" always; add_header Access-Control-Allow-Origin "*" always; # CORS for fonts and CSS specifically } ``` ### 16.8 Healthcare or HIPAA aware site ```nginx server { server_name patient-portal.example.com; include snippets/common-security-headers.conf; # Stricter referrer policy add_header Referrer-Policy "no-referrer" always; # Stricter CSP add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'; upgrade-insecure-requests" always; # Block all third party tracking add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), bluetooth=(), browsing-topics=(), interest-cohort=(), attribution-reporting=(), join-ad-interest-group=(), run-ad-auction=(), private-aggregation=()" always; } ``` ### 16.9 User upload serving endpoint (max security) ```nginx location /user-uploads/ { include snippets/common-security-headers.conf; # Force download, prevent any execution add_header Content-Type "application/octet-stream" always; add_header Content-Disposition "attachment" always; add_header X-Content-Type-Options "nosniff" always; add_header Content-Security-Policy "default-src 'none'" always; add_header Cross-Origin-Resource-Policy "same-origin" always; } ``` ### 16.10 thatwebhostingguy.com wildcard (no preload, isolated demos) ```nginx server { server_name *.thatwebhostingguy.com; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; # NO preload (different demos may not all support HTTPS) add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Permissive Permissions-Policy (demos may need various features) add_header Permissions-Policy "camera=(self), microphone=(self), geolocation=(self), interest-cohort=(), browsing-topics=()" always; # Wildcard demos: AdSense permitted add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' https:; img-src 'self' data: blob: https:; font-src 'self' data: https:; connect-src 'self' https:; frame-src 'self' https:; frame-ancestors 'self'; base-uri 'self'; object-src 'none'" always; } ``` --- ## 17. BUBBLES NGINX REFERENCE BLOCK (PASTE READY) The complete security header stanza, layered with caching (framework-http-caching-headers.md), content (framework-http-content-headers.md), and SEO (framework-http-seo-headers.md). ```nginx # /etc/nginx/snippets/common-security-headers.conf # Shared by every Bubbles client production site # Transport add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; # Content add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Capabilities add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), bluetooth=(), browsing-topics=(), interest-cohort=()" always; # Cross-origin (no isolation; just baseline hardening) add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Resource-Policy "same-site" always; ``` ```nginx # /etc/nginx/sites-available/example.com server { listen 80; listen [::]:80; server_name example.com www.example.com; return 301 https://example.com$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; http2 on; listen 443 quic reuseport; listen [::]:443 quic reuseport; server_name www.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; return 301 https://example.com$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; http2 on; listen 443 quic reuseport; listen [::]:443 quic reuseport; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; root /var/www/sites/example.com; index index.html; add_header Alt-Svc 'h3=":443"; ma=86400' always; # ===== HTML PAGES ===== location ~* \.html$ { include snippets/common-security-headers.conf; # Per site CSP add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://*.google-analytics.com https://*.googletagmanager.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; upgrade-insecure-requests" always; # Plus caching, content, SEO headers from other frameworks add_header Cache-Control "public, max-age=0, must-revalidate" always; add_header Content-Language "en-US" always; add_header X-Robots-Tag "max-snippet:200, max-image-preview:large, max-video-preview:-1" always; } # ===== STATIC ASSETS ===== location ~* \.(css|js|woff2|woff|ttf|otf)$ { include snippets/common-security-headers.conf; add_header Cross-Origin-Resource-Policy "same-site" always; add_header Cache-Control "public, max-age=31536000, immutable" always; } # ===== IMAGES ===== location ~* \.(jpg|jpeg|png|gif|webp|avif|svg|ico)$ { include snippets/common-security-headers.conf; add_header Cross-Origin-Resource-Policy "same-site" always; add_header Cache-Control "public, max-age=2592000" always; } # ===== PDF DOWNLOADS ===== location ~* \.pdf$ { include snippets/common-security-headers.conf; add_header Content-Disposition "attachment" always; add_header X-Robots-Tag "noindex" always; add_header Cache-Control "public, max-age=86400" always; } # ===== USER UPLOADS (max security) ===== location /user-uploads/ { include snippets/common-security-headers.conf; add_header Content-Type "application/octet-stream" always; add_header Content-Disposition "attachment" always; add_header Content-Security-Policy "default-src 'none'" always; add_header Cross-Origin-Resource-Policy "same-origin" always; add_header Cache-Control "public, max-age=86400" always; } # ===== API ENDPOINTS (FastAPI sidecar) ===== location /api/ { include snippets/common-security-headers.conf; add_header X-Robots-Tag "noindex, nofollow" always; proxy_pass http://127.0.0.1:9090; 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; # CSP and other headers may be overridden by upstream } # ===== ROOT ===== location / { include snippets/common-security-headers.conf; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://*.google-analytics.com https://*.googletagmanager.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; upgrade-insecure-requests" always; add_header Cache-Control "public, max-age=0, must-revalidate" always; try_files $uri $uri/ $uri.html =404; } } ``` After deploying: ```bash nginx -t && systemctl reload nginx ``` External validation: ```bash echo "https://observatory.mozilla.org/analyze/example.com" echo "https://securityheaders.com/?q=https://example.com/" echo "https://csp-evaluator.withgoogle.com/?csp=https://example.com/" ``` Target: A or A+ on both observatory.mozilla.org and securityheaders.com. --- ## 18. AUDIT CHECKLIST Run through these 60 items for any production site. ### Strict-Transport-Security 1. [ ] HTTPS responses include `Strict-Transport-Security` header. 2. [ ] HTTP responses do NOT include `Strict-Transport-Security` header. 3. [ ] max-age is at least 31536000 (1 year) for established sites. 4. [ ] `includeSubDomains` is present (after verifying all subdomains support HTTPS). 5. [ ] `preload` is present for sites submitted to or eligible for the preload list. 6. [ ] HTTP requests redirect to HTTPS with 301 (not 302). 7. [ ] Every subdomain serves HTTPS with a valid certificate. 8. [ ] certbot.timer is active and tested. ### Content-Security-Policy 9. [ ] CSP header is present on every HTML response. 10. [ ] `default-src` is set (fallback for unset directives). 11. [ ] `script-src` does not use `'unsafe-inline'` without justification. 12. [ ] `script-src` does not use `'unsafe-eval'` without justification. 13. [ ] If `'strict-dynamic'` is used, a fresh nonce is included on every response. 14. [ ] `object-src 'none'` is set (blocks Flash, deprecated plugins). 15. [ ] `base-uri 'self'` or `'none'` is set. 16. [ ] `form-action` is restricted appropriately. 17. [ ] `frame-ancestors` is set (replaces X-Frame-Options behavior). 18. [ ] `upgrade-insecure-requests` is set (during HTTPS migration). 19. [ ] CSP includes `report-uri` or `report-to` for violation monitoring. 20. [ ] CSP violations are monitored (not just emitted to /dev/null). 21. [ ] CSP rolled out in Report-Only mode first, then enforced. 22. [ ] CSP evaluator (csp-evaluator.withgoogle.com) shows no high severity issues. ### X-Frame-Options 23. [ ] X-Frame-Options is set on every HTML response. 24. [ ] Value is DENY or SAMEORIGIN (not ALLOW-FROM). 25. [ ] CSP frame-ancestors agrees with X-Frame-Options value. ### X-Content-Type-Options 26. [ ] `X-Content-Type-Options: nosniff` is set on every response. 27. [ ] All Content-Type values are accurate (see framework-http-content-headers.md). 28. [ ] User upload endpoints use `application/octet-stream` plus `attachment` disposition. ### Referrer-Policy 29. [ ] Referrer-Policy is set on every response. 30. [ ] Value is `strict-origin-when-cross-origin` or stricter. 31. [ ] Password reset and account pages use `no-referrer` or `same-origin`. ### Permissions-Policy 32. [ ] Permissions-Policy is set on every response. 33. [ ] Sensitive APIs (camera, microphone, geolocation, payment, USB, Bluetooth) are disabled unless needed. 34. [ ] `browsing-topics=()` is set (opt out of Topics tracking). 35. [ ] `interest-cohort=()` is set (historical FLoC opt out; harmless). ### Cross-Origin-Resource-Policy 36. [ ] CORP is set on assets, value appropriate to the use case. 37. [ ] Private user content uses `same-origin`. 38. [ ] Brand assets use `same-site`. 39. [ ] Publicly embeddable assets use `cross-origin`. ### Cross-Origin-Embedder-Policy 40. [ ] COEP is NOT set unless cross origin isolation is required. 41. [ ] If COEP is set, COOP is also set to `same-origin`. 42. [ ] If COEP is set, every cross origin resource has CORP or CORS. ### Cross-Origin-Opener-Policy 43. [ ] COOP is set on HTML responses. 44. [ ] Value is `same-origin` for typical sites. 45. [ ] OAuth flows use `same-origin-allow-popups`. ### Cross cutting 46. [ ] `nginx -t` passes without warnings. 47. [ ] `nginx -T` (effective config) shows all expected directives. 48. [ ] observatory.mozilla.org grade is A or A+. 49. [ ] securityheaders.com grade is A or A+. 50. [ ] CSP evaluator (csp-evaluator.withgoogle.com) shows no high severity issues. 51. [ ] Browser DevTools console shows no CSP violations on production. 52. [ ] All headers are sent with `always` (apply to error responses too). 53. [ ] Snippet include pattern is used (no add_header inheritance trap). 54. [ ] Headers consistent across all server blocks for the domain. 55. [ ] Headers consistent across HTTP/2 and HTTP/3. 56. [ ] Headers consistent across direct requests and FastAPI sidecar passthrough. 57. [ ] No header value contains user input without sanitization. 58. [ ] No header value contains protocol relative URLs. 59. [ ] No deprecated headers present (Public-Key-Pins, Expect-CT, Feature-Policy). 60. [ ] Site responds correctly to security scanner without producing false errors. A site that passes all 60 has correctly configured security and trust signal headers and will earn A or A+ grades from external scanners. --- ## 19. COMMON PITFALLS Fifteen patterns to recognize and avoid. **Pitfall 1: HSTS preload submitted before verifying subdomains.** Symptom: critical internal subdomain inaccessible permanently. Why it breaks: preload list takes months to remove from. Any subdomain serving only HTTP after preload is locked out. Fix prevention: audit every subdomain over HTTPS before submitting. Fix any HTTP only subdomain first. **Pitfall 2: CSP enforced without Report-Only test phase.** Symptom: GTM scripts blocked, analytics offline, third party widgets broken. Why it breaks: real world resource graphs always include unexpected origins. Fix prevention: Report-Only for at least a week, fix every violation, then enforce. **Pitfall 3: 'unsafe-inline' in script-src.** Symptom: XSS injection executes despite CSP. Why it breaks: `'unsafe-inline'` allows any inline script, defeating the primary protection. Fix: replace with nonce + strict-dynamic pattern. If pure static site, accept the lower security level. **Pitfall 4: X-Frame-Options without CSP frame-ancestors.** Symptom: scanner reports framing protection incomplete; some browsers do not enforce X-Frame-Options. Why it breaks: X-Frame-Options is being deprecated; CSP frame-ancestors is the authoritative replacement. Fix: set both for belt and suspenders. **Pitfall 5: nosniff missing on user upload serving endpoint.** Symptom: stored XSS via uploaded HTML file. Why it breaks: browser sniffs uploaded file as HTML, renders it, runs embedded scripts. Fix: set nosniff site wide AND serve user uploads with `application/octet-stream` plus `attachment` disposition. **Pitfall 6: Referrer-Policy unset, default leaks tokens.** Symptom: password reset tokens or session IDs appear in third party analytics logs. Why it breaks: default Referer header includes full URL with query string. Fix: set `strict-origin-when-cross-origin` site wide; `no-referrer` on sensitive endpoints. **Pitfall 7: Permissions-Policy blocks legitimate features.** Symptom: geolocation, camera, or other API silently refused. Why it breaks: directive value is `()` (block) instead of `(self)` or appropriate allowlist. Fix: set appropriate allowlist for features the site actually uses. **Pitfall 8: COEP enabled, all cross origin images blocked.** Symptom: layout broken, half the images missing. Why it breaks: third party CDN images do not have CORP or CORS. Fix: switch COEP to `credentialless`, OR self host the resources, OR add CORP at source. **Pitfall 9: COOP same-origin breaks OAuth flow.** Symptom: OAuth popup completes auth but cannot return data. Why it breaks: same-origin breaks window.opener. Fix: use `same-origin-allow-popups` instead. **Pitfall 10: HSTS on HTTP server block.** Symptom: nothing breaks but scanner notes anomaly. Why it breaks: HSTS over HTTP is ignored by the spec but indicates configuration confusion. Fix: remove HSTS from HTTP server blocks. HTTP server should only redirect. **Pitfall 11: CSP with no fallback (no default-src).** Symptom: some resource types unrestricted because their directive was not set. Why it breaks: directives not explicitly set inherit from default-src; without default-src, they are unrestricted. Fix: always set `default-src 'self'` as a baseline. **Pitfall 12: add_header inheritance trap on security headers.** Symptom: nosniff disappears on specific file types after a location block override. Why it breaks: nested `add_header` wipes parent declarations. Fix: snippet include pattern in every location. **Pitfall 13: Header value too long for nginx default buffer.** Symptom: nginx error or response truncated. Why it breaks: CSP with many origins exceeds default 8 KB header buffer. Fix: increase `large_client_header_buffers`, or simplify CSP using wildcards. **Pitfall 14: Permissions-Policy uses old Feature-Policy syntax.** Symptom: browser warning, header value ignored. Why it breaks: Permissions-Policy uses `feature=(allowlist)` not `feature 'self'`. Fix: convert to current syntax. **Pitfall 15: Mixed HTTP and HTTPS resources block on COEP.** Symptom: COEP blocks HTTP loaded images, fonts, scripts. Why it breaks: HTTP resources cannot send CORP or pass CORS checks. Fix: upgrade all resources to HTTPS; use CSP `upgrade-insecure-requests`. --- ## 20. DIAGNOSTIC COMMANDS Reference of every command useful for security header investigation. ### Inspect all security headers at once ```bash # Full security family curl -sI https://example.com/ | grep -iE "^(strict-transport-security|content-security-policy|x-frame-options|x-content-type-options|referrer-policy|permissions-policy|cross-origin-(opener|embedder|resource)-policy):" # Pretty print curl -sI https://example.com/ | grep -iE "^(strict-|content-security|x-|referrer|permissions|cross-origin):" | while read line; do KEY=$(echo "$line" | cut -d: -f1) VAL=$(echo "$line" | cut -d: -f2- | sed 's/^ //') echo "" echo "=== $KEY ===" echo "$VAL" | tr ';,' '\n' | sed 's/^ */ /' done ``` ### HSTS verification ```bash # Header presence and content curl -sI https://example.com/ | grep -i strict-transport-security # HTTP redirect check curl -sI http://example.com/ | head -3 # Preload eligibility curl -s "https://hstspreload.org/api/v2/status?domain=example.com" | python3 -m json.tool # All subdomains over HTTPS for sub in $(dig +short ANY example.com | grep -oE "^[a-z0-9-]+\.example\.com\."); do sub=${sub%.} echo -n "$sub: " curl -sI -m 5 "https://$sub/" 2>&1 | head -1 || echo "FAILED" done ``` ### CSP verification ```bash # Header presence curl -sI https://example.com/ | grep -i content-security-policy # Evaluator (Google's checker) echo "Visit: https://csp-evaluator.withgoogle.com/?csp=https%3A%2F%2Fexample.com%2F" # Report endpoint test curl -X POST https://example.com/csp-report \ -H "Content-Type: application/csp-report" \ -d '{"csp-report":{"document-uri":"https://example.com/","violated-directive":"script-src","blocked-uri":"https://evil.com/script.js"}}' # Check for unsafe directives curl -sI https://example.com/ | grep -i content-security-policy | grep -oE "unsafe-(inline|eval)" ``` ### Cross origin verification ```bash # All three cross origin headers curl -sI https://example.com/ | grep -iE "^cross-origin-" # Verify isolation in browser: # In DevTools console: window.crossOriginIsolated # Expected: true (if attempting isolation) # Verify SharedArrayBuffer: # typeof SharedArrayBuffer === "function" ``` ### External scanners ```bash # Mozilla Observatory echo "https://observatory.mozilla.org/analyze/example.com" # Security Headers echo "https://securityheaders.com/?q=https://example.com/" # Hardenize (comprehensive) echo "https://www.hardenize.com/report/example.com" # Internet.nl echo "https://internet.nl/site/example.com/" # Qualys SSL Labs (for TLS, not headers) echo "https://www.ssllabs.com/ssltest/analyze.html?d=example.com" ``` ### Server side investigation ```bash # All add_header directives in effect nginx -T 2>/dev/null | grep -iE "add_header.*(strict-transport|content-security|x-frame|x-content-type|referrer|permissions|cross-origin)" # Confirm includes work nginx -T 2>/dev/null | grep -i "include.*security" # Find any conflict (multiple values for same header) nginx -T 2>/dev/null | grep -i "add_header" | awk '{print $2}' | sort | uniq -c | sort -rn # Apply changes nginx -t && systemctl reload nginx ``` ### Browser DevTools quick reference 1. **Application or Storage panel**: shows HSTS state (`chrome://net-internals/#hsts`). 2. **Network panel**: shows headers for each request. Click any request, "Headers" tab. 3. **Console panel**: shows CSP violations in red. 4. **Issues panel** (Chrome): shows security issues with explanations. 5. **Security panel**: shows certificate, connection, and origin info. Useful console commands: ```javascript // Cross origin isolation status window.crossOriginIsolated // Available APIs that require isolation typeof SharedArrayBuffer typeof performance.measureUserAgentSpecificMemory // Trigger a CSP report (for testing report endpoint) eval("test") // Will trigger script-src violation if unsafe-eval not allowed ``` --- ## 21. CROSS-REFERENCES * [framework-http-caching-headers.md](framework-http-caching-headers.md): Cache-Control, ETag, Last-Modified, Expires, Vary, Age. Security headers should be cached with the response; do not cache security headers separately or stripped. * [framework-http-content-headers.md](framework-http-content-headers.md): Content-Type, Content-Language, Content-Encoding, Content-Length, Content-Disposition. nosniff pairs with strict Content-Type discipline. Content-Disposition is part of the user upload security pattern (Section 16.9 here). * [framework-http-seo-headers.md](framework-http-seo-headers.md): X-Robots-Tag, Link, Location. Open redirect security in framework-http-seo-headers.md Section 7.4 is part of the overall security posture. * [UNIVERSAL-RANKING-FRAMEWORK.md](UNIVERSAL-RANKING-FRAMEWORK.md): the master ranking reference. Security headers contribute to E-E-A-T trust signals. * [SEO-BUILD-REFERENCE.md](SEO-BUILD-REFERENCE.md) v2.4: the build playbook. * [framework-csp-deployment.md](framework-csp-deployment.md): deeper dive into CSP rollout strategy, nonce architecture, GTM integration. * [framework-tls-config.md](framework-tls-config.md): TLS cipher selection, certificate management, OCSP stapling. Pairs with HSTS for transport security. * Google CSP documentation: https://developers.google.com/tag-platform/security/guides/csp * web.dev strict CSP guide: https://web.dev/articles/strict-csp * HSTS preload list: https://hstspreload.org/ * MDN security headers reference: https://developer.mozilla.org/en-US/docs/Web/Security * Mozilla Observatory: https://observatory.mozilla.org/ * securityheaders.com: https://securityheaders.com/ * CSP Evaluator: https://csp-evaluator.withgoogle.com/ * RFC 6797 (HSTS): https://www.rfc-editor.org/rfc/rfc6797 * RFC 7034 (X-Frame-Options): https://www.rfc-editor.org/rfc/rfc7034 * W3C CSP Level 3: https://www.w3.org/TR/CSP3/ * W3C Referrer Policy: https://www.w3.org/TR/referrer-policy/ * W3C Permissions Policy: https://www.w3.org/TR/permissions-policy/ --- ## APPENDIX A: ONE PAGE QUICK REFERENCE For the person who just wants the answer. ### Bubbles default security header stack ```nginx # /etc/nginx/snippets/common-security-headers.conf add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), bluetooth=(), browsing-topics=(), interest-cohort=()" always; add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Resource-Policy "same-site" always; ``` ### Standard CSP for Bubbles client site with GTM and GA4 ``` Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://*.google-analytics.com https://*.googletagmanager.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; upgrade-insecure-requests ``` ### Header purpose table | Header | One line purpose | |---|---| | Strict-Transport-Security | Force HTTPS forever | | Content-Security-Policy | Allow list of where scripts and resources may come from | | X-Frame-Options | Block clickjacking (legacy) | | X-Content-Type-Options | Block MIME sniffing | | Referrer-Policy | Control what URL info leaks to other origins | | Permissions-Policy | Disable sensitive browser APIs | | Cross-Origin-Resource-Policy | Who may load this resource | | Cross-Origin-Embedder-Policy | What this document may embed | | Cross-Origin-Opener-Policy | What this document may share a window with | ### CSP value cheat sheet | Want | Use | |---|---| | Only same origin scripts | `script-src 'self'` | | Plus inline scripts safely | `script-src 'self' 'nonce-RANDOM' 'strict-dynamic'` | | Plus GTM | `script-src 'self' 'unsafe-inline' https://www.googletagmanager.com` | | Block all framing | `frame-ancestors 'none'` | | Same site framing | `frame-ancestors 'self'` | | Specific partner | `frame-ancestors 'self' https://partner.example.com` | | No plugins ever | `object-src 'none'` | | Auto upgrade HTTP refs | `upgrade-insecure-requests` | ### Five commands every operator should know ```bash # 1. View all security headers curl -sI https://example.com/ | grep -iE "^(strict|content-security|x-|referrer|permissions|cross-origin):" # 2. Check observatory score echo "Visit: https://observatory.mozilla.org/analyze/example.com" # 3. Check securityheaders.com score echo "Visit: https://securityheaders.com/?q=https://example.com/" # 4. Evaluate CSP echo "Visit: https://csp-evaluator.withgoogle.com/?csp=https://example.com/" # 5. Apply nginx changes nginx -t && systemctl reload nginx ``` ### Three end to end tests ```bash # 1. Verify HSTS is enforced curl -sI https://example.com/ | grep -i strict-transport-security # Expected: strict-transport-security: max-age=63072000; includeSubDomains; preload # 2. Verify no CSP unsafe directives (or that they are justified) curl -sI https://example.com/ | grep -i content-security-policy | grep -oE "unsafe-(inline|eval)" # If output appears, document why or remove # 3. Verify nosniff and X-Frame-Options site wide for path in / /about /services /blog /contact; do HEADERS=$(curl -sI "https://example.com$path" | grep -iE "x-content-type-options|x-frame-options") echo "=== $path ===" echo "$HEADERS" done # Both headers should appear on every page ``` If all three produce expected output AND observatory.mozilla.org returns A or A+, the security header stack is correctly wired. --- End of framework-http-security-headers.md.

Search equations, builds, manuscript text, and recovered sources.