
The security audit passed in March. Mozilla Observatory score: A+. HSTS preload submitted. CSP locked down with nonce-based script-src. In June, a CDN migration moved TLS termination to a new edge configuration. Nobody updated the origin header injection. By August, production responses were missing Strict-Transport-Security on 30% of paths, Content-Security-Policy had reverted to a permissive unsafe-inline default from the framework's dev template, and Access-Control-Allow-Origin: * had appeared on the API subdomain after a CORS "quick fix" in a hotfix branch.
The site was up. Uptime was green. SSL certificates were valid. The application had zero CVEs in the dependency scan. The security posture had silently regressed — and the only signal was a researcher's disclosure email.
HTTP security headers are not decorative. They are enforceable browser-side contracts: which scripts may run, whether the site may be framed, whether TLS is mandatory, which origins may call your API. They are also fragile — stripped by misconfigured reverse proxies, overwritten by CDN "optimization" passes, and replaced by framework defaults when deploy templates change.
This guide covers which headers matter in 2026, how they break on deploy, how to monitor them externally, and how monitoring supports SOC 2 and PCI-DSS evidence. By the end you'll have an assertion spec that catches header drift in 60 seconds.
Why Security Headers Strip on Deploy
Headers are set at multiple layers — and the effective response is whichever layer wins last (or first, depending on your stack):
- Application — middleware, framework defaults, Helmet.js, django-csp, etc.
- Reverse proxy — NGINX
add_header, ApacheHeader set - CDN edge — Cloudflare Transform Rules, CloudFront response headers policy
- Load balancer — ALB listener rules
Failure modes:
- CDN migration — new edge config doesn't replicate custom headers
- Proxy buffer change — NGINX
proxy_hide_headeraccidentally removes security headers from upstream - Framework upgrade — default security middleware disabled or renamed
- Environment leak — dev config (
CSP_REPORT_ONLY=false, permissive CORS) deployed to prod - Hotfix bypass — emergency deploy adds
Access-Control-Allow-Origin: *and never reverts - Static asset path — headers set on
/but not on/assets/*served from object storage - API subdomain —
api.example.commanaged separately, drifts fromwww
External monitoring hits the URLs users and browsers hit — the effective headers, not what your NGINX config file says they should be.
Headers That Matter in 2026
Strict-Transport-Security (HSTS)
Forces HTTPS for future visits. Example:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Failure: Header missing after CDN change → SSL stripping attacks possible on first visit.
Assert: Header present on HTTPS responses; max-age >= 31536000 for preload candidates.
See SSL Certificate Expiration Monitoring for the certificate layer.
Content-Security-Policy (CSP)
Controls which resources the browser may load. Example (simplified):
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none';
Too permissive: script-src * or unsafe-inline without nonce → XSS window open.
Too restrictive: Missing domain for analytics CDN → entire site JS broken (users see blank page, not a security alert).
Assert: Presence of header; deny-list assertions (unsafe-eval absent in prod); allow-list for known directives you require (frame-ancestors 'none' or 'self').
X-Frame-Options / frame-ancestors
Prevents clickjacking. Legacy:
X-Frame-Options: DENY
Modern equivalent in CSP: frame-ancestors 'none'.
Failure: Header removed → site embeddable in malicious iframe.
Assert: X-Frame-Options: DENY or SAMEORIGIN, or CSP frame-ancestors none/self.
X-Content-Type-Options
X-Content-Type-Options: nosniff
Stops MIME-type sniffing attacks.
Assert: Value exactly nosniff on HTML and API responses.
Referrer-Policy
Controls referrer leakage:
Referrer-Policy: strict-origin-when-cross-origin
Assert: Present; not unsafe-url in production.
Permissions-Policy (formerly Feature-Policy)
Disables browser features you don't use:
Permissions-Policy: geolocation=(), microphone=(), camera=()
Assert: Present on sensitive apps; camera/mic disabled unless required.
Cross-Origin-Opener-Policy (COOP) / Cross-Origin-Embedder-Policy (COEP)
Required for SharedArrayBuffer and some isolation guarantees:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Assert: If you depend on cross-origin isolation, both must be present and consistent.
CORS (Access-Control-*)
API headers:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Failure modes:
Access-Control-Allow-Origin: *with credentials — browser rejects; or wide open without credentials — data theft from any origin- Missing
Access-Control-Allow-Originon API after deploy → frontend CORS errors (often reported as "API down") - Overly broad
Allow-MethodsorAllow-Headers
Assert: Exact origin match (not * in production with cookies); Allow-Origin present on API preflight paths.
Pair with API Rate Limit Monitoring for API surface coverage.
Monitoring Approaches
1) Response header assertions (primary)
External monitor sends GET/HEAD to URL; inspects response headers (not body).
| Header | Assertion type |
|---|---|
| HSTS | Present; contains max-age= |
| CSP | Present; does not contain unsafe-eval (if policy forbids) |
| X-Frame-Options | Equals DENY or SAMEORIGIN |
| X-Content-Type-Options | Equals nosniff |
| Referrer-Policy | Present; not unsafe-url |
| CORS | Access-Control-Allow-Origin equals https://app.example.com |
Store a hash or canonical string of security-relevant headers. Alert on change.
2) Mozilla Observatory / securityheaders.com
Run quarterly scans for letter-grade baselines. Not suitable for per-minute monitoring (rate limits, point-in-time) but good for audit evidence.
3) CI post-deploy gate
After production deploy, script curls top 10 URLs and fails CI if assertions fail. Complements continuous external monitoring.
4) Subdomain matrix
Headers on www, api, app, cdn-origin, and authenticated paths (/dashboard) diverge. Monitor each independently.
5) Static vs dynamic paths
Object-storage-hosted /assets/ may lack headers entirely. Browsers treat script context separately — but HTML pages must have full set.
Compliance Angle
Security-header monitoring is a control auditors ask for:
- SOC 2 (CC6.1, CC7.2) — logical access and system operations; demonstrable that security configurations don't drift undetected. See SOC 2 Compliance Monitoring.
- PCI-DSS — requirement for secure configurations; headers are part of defence-in-depth. See PCI DSS Compliance Monitoring.
- GDPR — less direct, but breach prevention supports Article 32 security measures. See GDPR Compliance Monitoring.
Evidence pack: 90 days of monitoring logs showing header assertions passed, plus alert records for any drift events and remediation tickets.
Relationship to Other Security Monitoring
Headers are one layer:
- Defacement / malware — Website Security Monitoring catches content changes
- DDoS — DDoS Detection & Mitigation Monitoring catches traffic anomalies
- SSL — certificate expiry and chain validity
- Deploy safety siblings — Sitemap & robots.txt Monitoring, Redirect Chain Monitoring
- Health endpoints — Health Check Endpoint Design for liveness; headers are orthogonal
Alerting Thresholds That Work
Critical (page)
- HSTS missing on production HTTPS homepage
- CSP missing on authenticated app paths
Access-Control-Allow-Origin: *appears on API with credentialsX-Frame-Optionsremoved from login/dashboard- Security header hash changed on payment/checkout paths without change ticket
High (notification)
- Any security header missing from monitored URL set
- CSP contains newly added
unsafe-inlineorunsafe-eval - CORS origin widened (specific origin →
*) - Header set differs between regions (unintended geo split)
Informational
- Observatory grade dropped from A to B
- New header added (document intentional)
- Permissions-Policy tightened (verify no feature breakage)
Use Scheduled Maintenance Windows when intentionally relaxing headers during testing.
See Alert Fatigue: Notifications That Get Acted On.
Per-Header Failure Playbooks (Short)
| Symptom | Likely cause | Fix direction |
|---|---|---|
| HSTS gone | CDN config swap | Re-apply response headers policy at edge |
| CSP too loose | Framework default restored | Re-enable Helmet / security middleware |
| CSP breaks site | New third-party script | Add nonce or hash to script-src |
| CORS errors in browser | API deploy removed Allow-Origin | Restore explicit origin list |
| Clickjacking report | X-Frame-Options dropped | Restore DENY + frame-ancestors |
| API "down" | CORS preflight 404 | Fix OPTIONS handler + headers |
HTTP Security Headers Monitoring Checklist
- URL matrix: homepage, login, checkout, API root, static HTML
- HSTS asserted on all HTTPS entry points
- CSP present; production deny-list for
unsafe-eval(if policy requires) - X-Frame-Options or CSP frame-ancestors asserted
- X-Content-Type-Options: nosniff
- Referrer-Policy present and not unsafe-url
- CORS: explicit origin, no wildcard with credentials
- Header hash tracked; alert on change
- Multi-region probes (CDN geo headers)
- Post-deploy CI gate on top 10 URLs
- Quarterly Observatory / securityheaders.com scan for audit evidence
- Subdomains (
api.,app.) included - Paired with SSL monitoring and sibling deploy-safety posts
- Maintenance windows for intentional relaxations
How Webalert Helps With HTTP Security Headers Monitoring
Webalert monitors the headers browsers actually receive:
- HTTP monitoring — Poll production URLs every 1 minute; inspect response headers on every check
- Header assertions — Configure required headers and expected values:
Strict-Transport-Securitycontainsmax-age=31536000,X-Frame-OptionsequalsDENY,X-Content-Type-Optionsequalsnosniff - Content must not contain — For CSP, assert body/HTML separately; for headers, assert dangerous values absent (
Access-Control-Allow-Origin: *on authenticated API) - Change detection — Alert when any security header value changes from baseline hash
- Multi-region checks — Verify headers from EU, US, APAC — catch CDN geo misconfiguration
- Multi-channel alerts — Email, SMS, Slack, Discord, Microsoft Teams, webhooks
- Maintenance windows — Suppress during planned CDN experiments
- 5-minute setup — Add URL, add header rules, assign on-call
Example Webalert checks:
Homepage
- URL:
https://www.example.com/ - Status: 200
- Response header
Strict-Transport-Securitymust contain:max-age= - Response header
X-Content-Type-Optionsmust equal:nosniff - Response header
X-Frame-Optionsmust equal:DENY
API
- URL:
https://api.example.com/health - Response header
Access-Control-Allow-Originmust equal:https://app.example.com - Response header must not contain wildcard CORS on credentialed paths
See features and pricing for details.
Summary
- Security headers (HSTS, CSP, X-Frame-Options, CORS, etc.) enforce browser-side security contracts.
- They strip silently on CDN migrations, proxy misconfig, framework upgrades, and hotfixes — no 5xx, no app errors.
- Monitor externally with per-URL header assertions, hash-based change detection, and subdomain matrix coverage.
- Too-loose CSP/CORS is a vulnerability; too-strict is an outage — document your baseline and alert on drift.
- Compliance audits (SOC 2, PCI-DSS) benefit from continuous assertion logs and drift alerts.
- Integrate with SSL monitoring, defacement detection, and the deploy-safety trio (sitemap, redirects, headers).
Security posture is not a point-in-time audit score. It's a continuous invariant — headers your users' browsers must see on every response.