Staging vs Production Header Differences — How to Find What Is Missing
Updated April 2026
Security headers you carefully configured in staging often disappear in production — stripped by a CDN, load balancer, or reverse proxy. Here is how to find the exact difference in 30 seconds.
Why headers differ between environments
Your app server sets the headers. But in production, the response passes through layers that can modify or strip them:
- CDNs (Cloudflare, CloudFront) — may strip or override headers, add their own
- Load balancers — often strip custom headers unless explicitly forwarded
- Reverse proxies (Nginx, Traefik) — misconfigured
proxy_passcan drop add_header directives - WAFs — may modify security headers to avoid conflicts
The fastest way to find differences
Paste both URLs into Header Diff and it highlights every difference automatically — missing headers in red, value changes in orange, additions in green.
Compare headers between two URLs → Header DiffUsing curl to compare manually
# Staging curl -sI https://staging.yourapp.com | sort > staging-headers.txt # Production curl -sI https://yourapp.com | sort > prod-headers.txt # Diff diff staging-headers.txt prod-headers.txt
The most commonly dropped headers
| Header | Why it gets dropped | Fix |
|---|---|---|
| Content-Security-Policy | CDN strips large headers, or proxy_pass doesn't forward | Set at CDN layer (Transform Rules) not just origin |
| Strict-Transport-Security | Load balancer terminates SSL, strips HSTS | Set on the load balancer / CDN, not the backend |
| X-Frame-Options | Nginx proxy_pass without add_header in server block | Move add_header to the server{} block, not location{} |
| Permissions-Policy | Relatively new, often missed in CDN configs | Add via Transform Rules or response header policy |
Common Nginx mistake
In Nginx, add_header in a location{} block does not inherit from the parent server{} block. If you have add_header in any nested location, you must repeat all headers in that location:
# Wrong — CSP will be dropped in /api/ responses
server { add_header Content-Security-Policy "default-src 'self'"; location /api/ { add_header X-Custom "value"; # This wipes the CSP header proxy_pass http://backend; }
}
# Correct — repeat all headers
server { location /api/ { add_header Content-Security-Policy "default-src 'self'" always; add_header X-Custom "value" always; proxy_pass http://backend; }
}
Cloudflare stripping headers
If your origin sets security headers but Cloudflare strips them, set them in Cloudflare Rules → Transform Rules → Modify Response Header instead of relying on origin pass-through. This ensures headers are set at the edge regardless of origin behaviour.
Pre-deployment checklist
- Compare headers between
staging.yourapp.comandyourapp.comusing Header Diff - Run HeadersFixer on your production URL after every deploy
- Set headers at the CDN/edge layer for headers that must survive the entire stack
- Use
alwaysin Nginx add_header to ensure headers appear on error responses too