Why AWS CloudFront Strips Security Headers — And How to Add Them Back
CloudFront caches responses from your origin and may not forward all response headers to the browser. Security headers you set in Nginx or Express on your EC2 instance often never reach the user. There are two ways to fix this.
Why CloudFront strips headers
By default, CloudFront only forwards a subset of response headers from your origin. Headers it does not recognise or whitelist in the cache policy are dropped before the response reaches the browser.
Fix 1 — Response Headers Policy (suggested)
CloudFront has a built-in Response Headers Policy feature. Create a managed or custom policy and attach it to your distribution behaviours:
# Using AWS CLI
aws cloudfront create-response-headers-policy --response-headers-policy-config '{ "Name": "SecurityHeadersPolicy", "SecurityHeadersConfig": { "StrictTransportSecurity": { "Override": true, "IncludeSubdomains": true, "Preload": true, "AccessControlMaxAgeSec": 31536000 }, "ContentTypeOptions": { "Override": true }, "FrameOptions": { "FrameOption": "SAMEORIGIN", "Override": true }, "XSSProtection": { "Protection": false, "Override": true }, "ReferrerPolicy": { "ReferrerPolicy": "strict-origin-when-cross-origin", "Override": true } }, "CustomHeadersConfig": { "Quantity": 1, "Items": [{ "Header": "Permissions-Policy", "Value": "camera=(), microphone=(), geolocation=()", "Override": true }] }
}'
# Then attach to your distribution behaviour
aws cloudfront update-distribution --id YOURDISTRIID ...
In the AWS Console: CloudFront → Your Distribution → Behaviours → Edit → Response headers policy → Create policy or select "SecurityHeadersPolicy" (AWS managed).
Fix 2 — CloudFront Function
Add headers on the viewer response using a CloudFront Function (cheaper than Lambda@Edge for simple header manipulation):
// CloudFront Function — viewer-response event
function handler(event) { var response = event.response; var headers = response.headers; headers['strict-transport-security'] = { value: 'max-age=31536000; includeSubDomains; preload' }; headers['x-frame-options'] = { value: 'SAMEORIGIN' }; headers['x-content-type-options'] = { value: 'nosniff' }; headers['referrer-policy'] = { value: 'strict-origin-when-cross-origin' }; headers['permissions-policy'] = { value: 'camera=(), microphone=(), geolocation=()' }; return response;
}
Deploy: CloudFront → Functions → Create function → paste code → Publish → attach to distribution behaviour on Viewer Response event.
Verify headers reach the browser
# Check headers through CloudFront (not your origin directly) curl -I https://d1234abcd.cloudfront.net/ # or curl -I https://yoursite.com/ # Confirm headers like X-Frame-Options appear in the response
Use HeadersFixer — it fetches your live CloudFront URL and shows exactly which security headers are missing or misconfigured after CDN processing.
Verify your CloudFront headers → HeadersFixer