CloudFront Viewer Country Headers Missing in API Gateway Lambda
Fix CloudFront-Viewer-Country-Name, Region headers not reaching Lambda via API Gateway after Nov 2023 change. Configure Origin Request Policy, Lambda proxy integration, Regional API Gateway, or Lambda@Edge workaround for reliable forwarding.
CloudFront-Viewer-Country-Name headers stopped forwarding to Lambda via API Gateway in AWS
Setup:
Internet → CloudFront → API Gateway → Lambda
CloudFront distribution uses both Viewer Request and Origin Request Lambda@Edge functions.
Issue: Around November 18-21 (no deployments), the main Lambda stopped receiving:
CloudFront-Viewer-Country-NameCloudFront-Viewer-Country-RegionCloudFront-Viewer-Country-Region-Name
It still receives CloudFront-Viewer-Country correctly.
Details:
- Viewer Request and Origin Request Lambdas receive these headers fine.
- Cache policy allows all viewer headers.
- No Origin Request policy initially; adding one explicitly allowing these headers had no effect.
What caused this change, and how can I restore these headers to the main Lambda?
The short answer: CloudFront changed how it injects and forwards the extended viewer-location headers, and those detailed headers (CloudFront-Viewer-Country-Name, CloudFront-Viewer-Country-Region, CloudFront-Viewer-Country-Region-Name) will only reach your origin when CloudFront is explicitly configured to forward them and when API Gateway actually passes them to Lambda. In practice the fix is to (1) attach an Origin Request Policy that forwards those headers on the CloudFront behavior that hits API Gateway, and (2) ensure API Gateway forwards headers to your backend (use Lambda proxy integration or explicit header mappings) — or as a robust alternative use a Regional API Gateway fronted by your CloudFront (or copy values into non-CloudFront headers from your Origin Request Lambda@Edge as a quick workaround).
Contents
- Root cause: what changed
- How headers flow: CloudFront → API Gateway → Lambda
- Quick fixes to restore the headers now
- Recommended, robust solution (Regional API Gateway + CloudFront)
- Lambda@Edge workaround: copy to X- headers (code)
- API Gateway integration: mapping vs. Lambda proxy
- Diagnostics checklist: what to check and how to test
- Sources
- Conclusion
Root cause: what changed
Short story: around mid‑November 2023 CloudFront’s behavior for viewer-location headers changed: the extended viewer-location headers are created/injected at a later stage and CloudFront will not forward them to the origin unless a behavior has an Origin Request Policy (or a cache/origin policy that explicitly includes those headers) attached. That’s why your Viewer Request and Origin Request Lambda@Edge functions still see the headers (they run inside CloudFront’s edge pipeline), but the origin request that reaches API Gateway no longer includes those specific CloudFront-Viewer-* headers unless you explicitly forward them. See the AWS re:Post troubleshooting thread for the same symptom: Missing location headers from CloudFront to Lambda function.
Two extra complications that commonly cause people to see only CloudFront-Viewer-Country but not the other names:
-
API Gateway endpoint type. Edge‑Optimized API Gateway is itself fronted by an AWS-managed CloudFront distribution. That extra CloudFront layer (and custom domain behaviours) can strip or not preserve some
CloudFront-*headers; AWS documentation and community threads recommend using a Regional API Gateway + your own CloudFront if you need full control over headers. See: How to add CloudFront Viewer location headers to Edge‑Optimized API Gateway. -
API Gateway integration type. Non-proxy integrations only forward headers you explicitly map — so even if CloudFront forwards
CloudFront-Viewer-Country-Nameto API Gateway, API Gateway may not pass it on to Lambda unless it’s whitelisted/mapped or you use Lambda proxy integration. The community has many examples of that mapping requirement: StackOverflow — Accessing CloudFront headers in Lambda function.
So: the “no deployments, suddenly stopped” behavior is explained by service-side change in CloudFront header injection/forwarding plus the existing API Gateway configuration that wasn’t prepared for that change.
How headers flow: CloudFront, API Gateway, and Lambda
Understanding the pipeline helps pick the right fix.
-
Client → CloudFront (your distribution)
-
Viewer Request Lambda@Edge runs here (sees viewer headers).
-
CloudFront may inject viewer-location headers (timing changed; some added later).
-
Origin Request Lambda@Edge runs just before forwarding to origin (also sees headers and can modify them).
-
Which headers are forwarded to origin is controlled by the CloudFront behavior’s Origin Request Policy (or a cache policy that also forwards headers).
-
CloudFront → API Gateway
-
If API Gateway is Edge‑Optimized, the request may hit an AWS-managed CloudFront in front of API Gateway (additional hop).
-
That hop can drop or not forward certain
CloudFront-*headers. -
API Gateway → Lambda
-
With Lambda proxy integration: API Gateway forwards all request headers to the Lambda event.
-
With non-proxy integration: API Gateway forwards only headers explicitly mapped in Method/Integration Request settings.
Implication: to get CloudFront-Viewer-Country-Name into Lambda you must (A) have CloudFront forward the header to the API Gateway origin, and (B) have API Gateway forward it to Lambda.
For general guidance on forwarding headers from CloudFront, see the AWS knowledge article: Configure CloudFront to forward the host header to the origin (the same console area controls other headers).
Quick fixes to restore the headers now
Try these in order — they range from fastest to more robust.
- Confirm the CloudFront behavior forwards the viewer headers
- CloudFront Console → Distributions → Behaviors → Edit the behavior that matches your API path.
- Under Origin request policy select a custom policy that forwards the headers you need. Create a policy that includes:
CloudFront-Viewer-Country-NameCloudFront-Viewer-Country-RegionCloudFront-Viewer-Country-Region-Name- (optionally)
CloudFront-Viewer-Country - Save and wait for distribution to deploy.
- Important: use an Origin Request Policy to forward headers to the origin; a Cache Policy controls cache-key behaviour (you usually want these forwarded to origin but not used for cache key to avoid cache fragmentation).
- Ensure API Gateway forwards them to Lambda
- If you can, use Lambda proxy integration (recommended). That forwards all headers automatically.
- If you must use non-proxy integration, add the header names in Method Request → Add request header, then in Integration Request map them to integration.request.header.
. Example walkthroughs are on StackOverflow: Accessing CloudFront viewer country header via API Gateway HTTP proxy?.
- If you’re using an Edge‑Optimized API Gateway or a custom domain
- The simplest robust fix is to switch to a Regional API Gateway and put your CloudFront distribution in front of it (you control the CloudFront → origin behavior). See the AWS re:Post thread explaining why Edge‑Optimized endpoints are problematic for viewer headers: How to add CloudFront Viewer location headers to Edge‑Optimized API Gateway.
- Quick workaround (no API Gateway changes)
- Since your Origin Request Lambda@Edge already sees the headers, have that Lambda copy the CloudFront header values into safe custom headers (for example
X-Viewer-Country-Name,X-Viewer-Country-Region) that won’t be stripped. Then ensure your Origin Request Policy forwards thoseX-headers to the origin (and API Gateway maps/forwards them to Lambda). This is often the fastest way to regain the values site-wide.
If “I already added an origin policy and nothing changed”: double-check you attached it to the correct behavior and origin, that the distribution status finished deploying, and that API Gateway is not the component dropping the headers (test by logging incoming headers in a simple Lambda).
Recommended, robust solution (Regional API Gateway + CloudFront)
If you need stable, long-term behavior:
- Convert to a Regional API Gateway (or create one next to the existing API). Regional endpoints aren’t fronted by AWS-managed CloudFront, so you won’t have the opaque extra hop that can strip headers.
- Put your own CloudFront distribution in front of the Regional API Gateway. In your distribution:
- Attach a proper Origin Request Policy that forwards the
CloudFront-Viewer-*headers to the API origin. - Use a Cache Policy that does NOT include those headers in the cache key (unless you want per-country caching).
- Use Lambda proxy integration in API Gateway so all headers are forwarded to the Lambda event.
- Optionally, remove Lambda@Edge complexity unless you need real-time edge processing — moving logic to a single Lambda can simplify testing and debugging.
This approach gives you full control of which headers are forwarded, how caching works, and simplifies debugging (no mysterious AWS-managed CloudFront in the middle). The same recommendation appears in AWS community guidance on missing CloudFront headers when using Edge‑Optimized endpoints: API Gateway + Custom Domain Name = Missing CloudFront headers.
Lambda@Edge workaround: copy to X- headers (code)
If you prefer a quick operational fix, modify your Origin Request Lambda@Edge to copy the CloudFront viewer headers into safe X- headers that you control. Example Node.js (Origin Request):
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers || {};
// header keys are lowercase in Lambda@Edge
const get = (name) => (headers[name.toLowerCase()] && headers[name.toLowerCase()][0].value) || null;
const country = get('cloudfront-viewer-country');
const countryName = get('cloudfront-viewer-country-name');
const region = get('cloudfront-viewer-country-region');
const regionName = get('cloudfront-viewer-country-region-name');
if (countryName) {
headers['x-viewer-country-name'] = [{ key: 'X-Viewer-Country-Name', value: countryName }];
}
if (region) {
headers['x-viewer-country-region'] = [{ key: 'X-Viewer-Country-Region', value: region }];
}
if (regionName) {
headers['x-viewer-country-region-name'] = [{ key: 'X-Viewer-Country-Region-Name', value: regionName }];
}
request.headers = headers;
callback(null, request);
};
After deploying that, update your CloudFront Behavior’s Origin Request Policy to forward the X-Viewer-* headers to the origin. Then in API Gateway either use Lambda proxy or map these X- headers into the Lambda event.
This avoids relying on CloudFront-* header passthrough and is useful when API Gateway’s fronting CloudFront strips CloudFront-* headers.
API Gateway integration: mapping vs. Lambda proxy
Which mode should you use?
-
Lambda proxy integration — recommended:
-
Pros: forwards all headers automatically (less config work), consistent event format, easier to log & debug.
-
Cons: you handle request parsing inside Lambda.
-
Non-proxy (custom) integration:
-
You must explicitly declare and map request headers in Method Request and Integration Request. Example mapping (integration.request.header) is required for each header you want in the backend event. Community examples: StackOverflow thread on accessing CloudFront headers in Lambda via API Gateway.
If you currently have non-proxy integration and don’t want to change, add Method Request headers and then map them in Integration Request to integration.request.header.<name> — or change to proxy integration for simplicity.
A note about header names: API Gateway treats headers case-insensitively, but mapping templates often reference exact header names (use the same names you forward from CloudFront or the X- names you created).
Diagnostics checklist: what to check and how to test
Follow this checklist to find where the header is lost:
- Reproduce and log:
- Add a simple Lambda that logs
event.headers(or add logging to your existing Lambda). Call the CloudFront URL. Check CloudWatch logs to see what headers arrive at Lambda.
- If headers are missing in Lambda:
- Check API Gateway logs: enable execution logging / access logs for your stage to see incoming headers to API Gateway.
- Confirm API Gateway endpoint type: Edge‑Optimized vs Regional. If Edge‑Optimized, that’s a likely reason headers are stripped.
- Check CloudFront behavior:
- Confirm the Origin Request Policy is attached to the exact behavior that serves the API path (pattern matching).
- If you have multiple behaviors or origins (e.g., root path vs /api/*), you might have attached the policy to the wrong one.
- Test Origin Request Lambda@Edge:
- In your Origin Request Lambda@Edge, add a temporary
X-Debug-Header: <value>so you can verify whether that value reaches API Gateway/Lambda. If the debug X header arrives, but theCloudFront-*headers don’t, that confirms the extra CloudFront/API Gateway hop is strippingCloudFront-*headers.
- Time/propagation:
- CloudFront deployments take time; after changing behavior or origin request policy wait for distribution to finish deploying.
- Cache vs origin policy:
- If you forward headers to origin but see cache fragmentation, adjust Cache Policy so these headers are forwarded to origin but not included in the cache key (if that’s what you want).
If you’d like, follow these diagnostic steps and paste the relevant CloudWatch logs (sanitized) and I can point to exactly which component is dropping the headers.
Sources
- Missing location headers from CloudFront to Lambda function — AWS re:Post
- Accessing AWS CloudFront headers in Lambda Function — Stack Overflow
- Accessing cloudfront-viewer-country header in AWS API Gateway using HTTP Proxy — Stack Overflow
- How to add CloudFront Viewer location headers to Edge‑Optimized API Gateway — AWS re:Post
- Lambda, cloudfront: cloudfront-forwarded-address missing from headers — Reddit r/aws
- API Gateway + Custom Domain Name = Missing CloudFront headers — AWS re:Post
- Configure CloudFront to forward the host header to the origin — AWS knowledge center (repost)
- AWS API Gateway with Authorization behind CloudFront — Medium (practical context)
Conclusion
Your CloudFront-Viewer-Country-Name/-Region/-Region-Name headers disappeared because CloudFront’s header injection/forwarding behavior changed and those extended viewer-location headers are only forwarded to origins when you explicitly configure CloudFront (Origin Request Policy) and when API Gateway forwards them to Lambda (Lambda proxy or mapped headers). Short-term: attach an Origin Request Policy and/or copy the values into custom X- headers in your Origin Request Lambda@Edge; medium/long-term: use a Regional API Gateway fronted by your CloudFront and use Lambda proxy integration to ensure consistent header delivery to your Lambda.