← Blog  ·  October 2026

Security Header Regression: Why Headers Disappear After Deployments

Security headers are easy to add once. They are surprisingly easy to lose — silently, without any error, often discovered weeks later by a bug bounty researcher or an automated scanner.

You spent an afternoon adding Strict-Transport-Security, Content-Security-Policy, and X-Frame-Options to your nginx config. Your security scanner gave you an A. Three months later you deploy a new CDN configuration and your grade drops to a D. Nobody noticed.

This is security header regression — and it is far more common than most developers realize.

🔍 Check your headers right now: Free security headers checker — see your current grade in seconds →

Why security headers regress

The most common causes of security header regression, in rough order of frequency:

1. CDN or reverse proxy configuration changes

Security headers are often set at the edge (Cloudflare, Fastly, AWS CloudFront) rather than in the application itself. When you update a CDN config, create a new distribution, or change cache rules, header directives can silently disappear. The deployment succeeds with zero errors — because from the server's perspective, nothing failed.

⚠️ Cloudflare Page Rules and Transform Rules: If your headers are set via Cloudflare Transform Rules and you add a new Route or Worker that intercepts the same path, the transform rules may no longer apply. This is a particularly common source of HSTS and CSP regressions.

2. nginx or Apache config merges

Your security headers might live in a location / block. When you deploy a new server block or a new vhost, the add_header directives from the parent block may not propagate into nested location blocks — this is a known nginx behavior. Adding one innocent location block can silently drop all headers from that path.

# This works for /, but adding a nested block can break header inheritance:
server {
    add_header X-Frame-Options "SAMEORIGIN";
    add_header Content-Security-Policy "default-src 'self'";

    location / {
        try_files $uri $uri/ =404;
    }

    # Adding this block causes the above headers to stop applying to /api/:
    location /api/ {
        proxy_pass http://localhost:3000;
        # The add_header directives above are now NOT sent for /api/ requests
    }
}

The fix is to repeat add_header in every nested location block, or to use always in a way that applies globally. But the failure mode is silent — your / route still returns headers, just not /api/ ones.

3. Load balancer changes

AWS ALB, GCP Cloud Load Balancing, and Azure Application Gateway can all be configured to add or strip response headers. When infra teams change load balancer rules, security headers can vanish from production without any application change. These updates don't trigger app deployment pipelines, so they don't show up in deploy logs.

4. Moving between hosting platforms

Migrating from Heroku to Render, from Nginx to Caddy, or from a custom server to Vercel often resets security headers to defaults. Each platform has its own mechanism for setting response headers, and headers from the previous platform are invisible to the new one.

5. Framework and middleware upgrades

If your headers are set by a middleware library (helmet.js, Django SecurityMiddleware, Spring Security), a major version upgrade can change defaults. Helmet v5 shipped with different defaults than Helmet v4. Upgrading silently changed what helmet() with no arguments returned.


Why nobody notices

Security header regressions are invisible by default. Unlike a broken API endpoint that returns 500 errors, a missing security header:

The consequence of a missing Content-Security-Policy is that an XSS attack that would have been blocked can now succeed. The consequence of a missing Strict-Transport-Security header is that users on insecure networks are vulnerable to SSL stripping. These consequences are probabilistic and delayed — so the regression goes unnoticed until a security audit or a bug bounty report.

How long do regressions go undetected? In practice, security header regressions often go undetected for months. They tend to surface during annual pen tests, Qualys SSL Labs scans, or Google Search Console security alerts — not during the deployment that introduced them.

How to detect security header regressions

Option 1: Check manually after each deployment

The lowest-effort option that requires the most discipline. After every deployment, run:

curl -s -I https://yourdomain.com | grep -i -E "strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy"

Or use a free checker like the security headers checker to see your current grade at a glance.

The problem: this only works if you remember to check. Regressions caused by infrastructure changes (CDN reconfigs, load balancer updates) often happen outside of app deployment cycles, so "check after each deploy" misses them entirely.

Option 2: Add security header assertions to your CI pipeline

A better approach is to assert specific header values in integration tests or as a post-deploy smoke test:

# In your CI pipeline (curl-based smoke test)
HEADERS=$(curl -s -I https://staging.yourdomain.com)

assert_header() {
  if ! echo "$HEADERS" | grep -qi "$1"; then
    echo "MISSING SECURITY HEADER: $1"
    exit 1
  fi
}

assert_header "strict-transport-security"
assert_header "content-security-policy"
assert_header "x-frame-options"
assert_header "x-content-type-options"
echo "All security headers present ✓"

This catches regressions introduced by application deployments. It still misses infrastructure-level changes.

Option 3: Continuous header monitoring with alerts

The most reliable approach is to check your headers from outside your infrastructure on a regular schedule — and send an alert if anything changes or disappears.

This catches regressions from all causes: application deployments, CDN changes, load balancer updates, certificate renewals that reset server configs, and anything else that touches your response headers.

🛡️ Get alerted when your security headers change

HeaderGuard monitors your HTTP security headers daily and sends you an email the moment a header disappears or its value changes — before a security scanner or bug bounty hunter finds it for you.

No agents to install, no CI pipeline changes. Just add your domain and HeaderGuard handles the rest.

Join the HeaderGuard waitlist → Check your headers now (free)

Which headers to monitor

Focus on the six headers that carry the most security weight:

HeaderWhat you lose when it disappears
Strict-Transport-SecurityHTTPS enforcement — users become vulnerable to SSL stripping on insecure networks
Content-Security-PolicyXSS mitigation — script injection attacks that were blocked can now succeed
X-Frame-OptionsClickjacking protection — your pages can be embedded in malicious iframes
X-Content-Type-OptionsMIME sniffing prevention — browsers may execute content as a different type than intended
Referrer-PolicyReferrer leakage — full URLs (potentially with tokens or PII) may leak to third-party sites
Permissions-PolicyFeature access — camera, microphone, and geolocation permissions revert to browser defaults

The most impactful regressions to catch immediately are Strict-Transport-Security (direct MITM risk) and Content-Security-Policy (XSS mitigation). The others reduce your security posture but are less immediately exploitable.


How HeaderGuard monitors for regressions

HeaderGuard checks your domain's security headers daily from external infrastructure — outside your own servers, CDN, and load balancers. When any monitored header changes or disappears, it sends you an email immediately.

The key difference from CI pipeline checks: HeaderGuard catches infrastructure-level regressions that never touch your application code or deployment pipeline. CDN configuration updates, load balancer rule changes, and certificate renewals that reset server configs are all visible to an external monitor — and invisible to a deployment-triggered smoke test.

Check your current security headers → to see your baseline, then join the HeaderGuard waitlist to get alerted when anything changes.