Headers

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.

Common symptom
Headers appear in curl requests directly to your EC2 IP but are missing when you curl through the CloudFront distribution URL. The headers exist on your origin — CloudFront is dropping them.

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