CloudFront Not Forwarding Headers to Origin? Fix It With an Origin Request Policy: AllViewer and Managed Policy IDs, Host, Authorization, X-Forwarded-For and CORS
When CloudFront is not forwarding headers to your origin, it is usually doing exactly what it was told. With today's cache policies, a request that misses the cache reaches your origin with almost nothing from the viewer: the URL path, the body, and a few headers CloudFront adds itself (Host set to your origin's domain, User-Agent replaced with "Amazon CloudFront", X-Amz-Cf-Id and X-Forwarded-For). Query strings, cookies and every other viewer header, including Authorization, Origin, Accept-Language and the real Host, are left out unless a policy says otherwise. The fix is one setting on the cache behavior: an origin request policy sends values to the origin without caching on them, and a cache policy both caches on them and sends them. Most APIs want the managed AllViewer policy (or AllViewerExceptHostHeader for API Gateway and Lambda function URLs) together with CachingDisabled. The one rule that matters more than any other: if you forward Authorization to the origin but leave it out of the cache key, CloudFront can serve one signed-in user's cached response to another user. Either put it in the cache key or do not cache that path at all. This page covers every header people try to forward, the managed policy IDs for scripts and Terraform, X-Forwarded-For and the viewer's real IP, CORS, custom origin headers, size limits and the legacy settings.
Jake's shop moved its booking API behind CloudFront to speed up the website, and three things broke on the same morning: every signed-in request came back "unauthorized", the price page forgot which country customers were in, and the server logs showed every visitor arriving from a handful of Amazon addresses. His developer friend Ethan opened the distribution and found the default cache behavior using a caching-optimized policy and no origin request policy at all. "So CloudFront ate the headers," Jake said. "CloudFront was never told to pass them on," Ethan said. This page is the fix for each of the three, and the one mistake Ethan nearly made while fixing the first.
New to CloudFront? A distribution sits in front of your origin (an S3 bucket, a load balancer, API Gateway, a server) and answers from its edge caches when it can. Each cache behavior (the default one, plus any with path patterns such as /api/*) has its own policies, and that is where everything on this page is set.
How CloudFront decides what reaches your origin
When a viewer request misses the cache, CloudFront builds a new request to your origin, the origin request. Two policies on the cache behavior decide what goes into it:
| Cache policy | Origin request policy | |
|---|---|---|
| Decides | The cache key: which headers, cookies and query strings make two requests "different" | Extra values sent to the origin without becoming part of the cache key |
| Sent to origin? | Yes, everything in the cache key is automatically forwarded | Yes, that is its whole purpose |
| Effect on cache hit ratio | Every value added splits the cache into more copies | None |
| Also sets | TTLs (minimum, default, maximum) and compression | Nothing else |
| Required? | Yes, one per behavior | Optional (none means nothing extra is forwarded) |
The rule of thumb: if a header changes what the origin returns (a language, a country price, a signed-in user's data), it belongs in the cache key, so different viewers get different cached copies. If the origin only needs to see it (for logging, analytics, request tracing, or because responses are not cached anyway), put it in the origin request policy and keep the cache efficient. There is a third, older mode, legacy cache settings, where you list headers to forward directly on the behavior; in that mode CloudFront forwards headers to your origin by default, which is why advice written for it does not match what you see today.
What your origin receives by default (and why that surprises people)
With a cache policy and no origin request policy, the origin request always contains the URL path (without the query string or domain), the request body if there is one, and the headers CloudFront handles itself:
Host: set to your origin's domain name, not your website's domain.User-Agent: replaced withAmazon CloudFront.X-Amz-Cf-Id: an encrypted ID CloudFront adds to identify the request.X-Forwarded-For: the viewer's IP address, added or appended (see client IP).Via: forwarded, identifying CloudFront.
Everything else from the viewer is left out unless a policy includes it: query strings (so ?page=2 never reaches the origin), cookies (so sessions break), and viewer headers such as Authorization, Accept-Language, Referer and any X- header your front end sends. A handful of protocol headers, such as Range and the If-* conditional headers, follow CloudFront's own rules; the next section lists them one by one. That default is right for static files, which do not care about any of them, and wrong for almost every API. Most "CloudFront is not forwarding headers" questions are a dynamic application behind a static-file configuration.
How CloudFront treats each standard request header
When you search for a specific header, this is the table you want. It shows what CloudFront does with each header when no policy asks for it, and whether you are allowed to put it in the cache key. "Removes" means your origin never sees it unless a policy includes it.
| Header | If no policy includes it | Can be in the cache key? |
|---|---|---|
Accept, Accept-Charset, Accept-Language, Referer | Removed | Yes |
Accept-Encoding | A normalized version is forwarded when the value contains gzip or br | Yes |
Authorization | Removed on GET and HEAD (and on OPTIONS when OPTIONS is cached); kept on DELETE, PATCH, POST, PUT | Yes |
Cookie | Removed unless cookies are forwarded | Through the cookie settings, not as a header |
Host | Set to the origin's domain name | Custom origins yes; S3 and MediaStore no |
User-Agent | Replaced with Amazon CloudFront | Yes, but not recommended |
Connection | Replaced with Connection: Keep-Alive | No |
Range, If-Match, If-None-Match, If-Modified-Since, If-Range, If-Unmodified-Since | Forwarded | Yes |
Cache-Control, Content-Length, Pragma, Max-Forwards, Transfer-Encoding | Forwarded | No |
Upgrade | Removed, except on WebSocket connections | No |
X-Forwarded-Proto, X-Real-IP, Expect, Proxy-Authorization, TE, Trailer, X-Edge-* | Removed | No (Expect: yes) |
X-HTTP-Method-Override | Removed | Yes |
X-Forwarded-For | Forwarded, with the viewer's IP appended | Yes |
X-Amz-Cf-Id | Added by CloudFront (a unique, encrypted request ID) | No |
Two rows explain a lot of confused questions. X-Forwarded-Proto is removed, so an application that checks it to decide "was this HTTPS?" sees nothing and may loop on redirects; use CloudFront-Forwarded-Proto instead (covered below). And X-HTTP-Method-Override is removed, which quietly breaks older clients that tunnel PUT or DELETE through POST until you add it to a policy. CloudFront also talks to custom origins over HTTP/1.1, whatever HTTP version the viewer used, so CloudFront-Viewer-Http-Version is the only way your origin learns the viewer's version.
The managed origin request policies, with their IDs
AWS provides ready-made policies, so most distributions need no custom policy at all. Use the ID in CloudFormation, the CLI, the API or Terraform; in the console you pick them by name.
| Origin request policy | Forwards | Use for | ID |
|---|---|---|---|
AllViewer | All viewer headers, cookies and query strings | ALB, EC2 or container APIs that need everything | 216adef6-5c7f-47e4-b989-5492eafa07d3 |
AllViewerExceptHostHeader | All viewer values except Host, plus protocol, HTTP and TLS version, device and location headers | API Gateway, Lambda function URLs, Lambda Web Functions | b689b0a8-53d0-40ab-baf2-68738e2966ac |
AllViewerAndCloudFrontHeaders-2022-06 | All viewer values plus every CloudFront header released through June 2022 | Origins that want country, device and protocol information too | 33f36d7e-f396-46d9-90e0-52428a34d9dc |
CORS-S3Origin | Origin, Access-Control-Request-Headers, Access-Control-Request-Method | S3 buckets with a CORS configuration | 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf |
CORS-CustomOrigin | Origin | Custom origins that answer CORS themselves | 59781a5b-3903-41f3-afcb-af62929ccde1 |
HostHeaderOnly | Only Host | Origins that route by domain name (virtual hosts, ALB host rules) | bf0718e1-ba1e-49d1-88b1-f726733018ae |
UserAgentRefererHeaders | Only User-Agent and Referer | Analytics or bot rules at the origin | pick by name |
One S3 detail: with the managed AllViewer, AllViewerAndCloudFrontHeaders-2022-06 and AllViewerExceptHostHeader policies, CloudFront does not forward the response-content-type, response-content-disposition or response-content-encoding query strings to Amazon S3 origins, because they change the headers S3 returns. Every other query string still goes through. If your download links depend on those parameters (a common way to force "Save as" with a file name), create a custom origin request policy that includes them; custom policies forward them as configured.
And the managed cache policies you will pair them with:
| Cache policy | Cache key and TTLs | Use for | ID |
|---|---|---|---|
CachingOptimized | Path plus normalized Accept-Encoding only; TTL 1 s min, 24 h default, 365 days max | Static files | 658327ea-f89d-4fab-a63d-7e88639e58f6 |
CachingDisabled | Nothing cached; all TTLs 0 | APIs and per-user pages | 4135ea2d-6df8-44a3-9df3-4b5a84be39ad |
CachingOptimizedForUncompressedObjects | Like CachingOptimized, compression off | Already-compressed media | b2884449-e4de-46a7-ac36-70bc7f1ddd6d |
UseOriginCacheControlHeaders | Host, Origin, the method-override headers and all cookies; TTL 0 default, up to 365 days from the origin's Cache-Control | Dynamic sites whose origin sets Cache-Control | 83da9c7e-98b4-4e11-a168-04f0df8e2c65 |
UseOriginCacheControlHeaders-QueryStrings | The same, plus all query strings | The same, when query strings change the page | 4cc15a8a-d715-48a4-82b8-cc0b614638fe |
Amplify | Authorization, CloudFront-Viewer-Country, Host, all cookies and query strings; TTL 2 s default, 10 min max | AWS Amplify web apps | 2e54312d-136d-493c-8eb9-b001f22f67d2 |
The two UseOriginCacheControlHeaders policies are the quiet heroes for websites with a mix of pages. Because Host and all cookies are already in their cache key, a signed-in visitor with a session cookie never gets another visitor's copy, and because the default TTL is 0, nothing is cached unless your origin sends a Cache-Control header that says so. You keep control in your application: send Cache-Control: public, max-age=300 on the pages that are the same for everyone and private or no-store on the rest. The Amplify policy is a good example of a cache key built for per-user safety: Authorization, country and Host are all in it.
A warning from the policy descriptions themselves: because CachingOptimized has a minimum TTL above zero, CloudFront caches for at least that long even when the origin says Cache-Control: no-cache, no-store or private. Never put a per-user API behind it.
Which policy pair to use for your origin
If you only want the answer for your setup, start here. Each row is a common origin with the pair that forwards what it needs and nothing that hurts it. Where a page mixes static files and an API, split it into two behaviors and use two rows.
| Origin and content | Cache policy | Origin request policy |
|---|---|---|
| S3 bucket, static website files | CachingOptimized | None |
| S3 bucket used by another site's JavaScript (CORS) | CachingOptimized, or a custom policy with Origin in the key | CORS-S3Origin |
| API Gateway REST or HTTP API | CachingDisabled | AllViewerExceptHostHeader |
| Lambda function URL | CachingDisabled | AllViewerExceptHostHeader |
| ALB, EC2 or ECS API with sign-in | CachingDisabled | AllViewer |
| ALB, EC2 or ECS website (WordPress and similar) that sets Cache-Control | UseOriginCacheControlHeaders(-QueryStrings) | AllViewer |
| AWS Amplify web app | Amplify | AllViewerExceptHostHeader |
Two things to notice. The origin request policy only adds; it never removes what the cache policy already forwards. So pairing CachingOptimized with AllViewer still caches on the path alone, which is exactly the Authorization leak in the section below. And none of these rows forward the viewer's Host to API Gateway or Lambda, for the reason the next section explains.
Forwarding the Host header: when to, and when it breaks things
By default the origin sees Host: your-origin-domain. Whether to forward the viewer's own Host (your website's domain) depends entirely on the origin:
- Forward it to origins that decide what to serve by domain name: an ALB with host-based listener rules, a web server with several virtual hosts, an application that builds absolute links or redirects from the Host it sees.
AllViewerorHostHeaderOnlydo it. - Never forward it to API Gateway, Lambda function URLs or Lambda Web Functions. They expect Host to be their own domain; the viewer's Host makes them reject the request (API Gateway typically answers 403). Use
AllViewerExceptHostHeader, which removes the viewer's Host and lets CloudFront add the origin's. - You cannot forward or cache on Host for Amazon S3 origins; S3 always receives the bucket's domain.
If you forward Host to an origin over HTTPS, the origin's certificate must also be valid for your website's domain, because CloudFront then connects using that name. An ALB with a certificate only for its own *.elb.amazonaws.com name fails the TLS check, which shows up as a 502. Our custom domain guide covers putting the right certificate on both sides.
Forwarding the Authorization header without leaking cached data
CloudFront removes Authorization from GET and HEAD requests before they reach the origin (and from OPTIONS if you cache OPTIONS responses); it leaves it on DELETE, PATCH, POST and PUT. That is why a signed-in API often works for saving data but fails for reading it. You have three ways to make the origin receive it:
- Add
Authorizationto the cache key in a custom cache policy. Each token gets its own cached copy, and the header is forwarded automatically. - Add
Authorizationindividually to an origin request policy. - Use an origin request policy that forwards all viewer headers, such as
AllViewer.
Options 2 and 3 come with the warning that matters most on this page. If the origin receives Authorization but the cache key does not include it, CloudFront can cache the response for user A and serve that same cached response to user B, or to a viewer with no token at all. For anything per-user, pair options 2 or 3 with CachingDisabled on that path, or use option 1 so the token is part of the key.
This is the mistake Ethan nearly made. His first fix was AllViewer on the existing caching-optimized behavior: sign-in worked, and the "My repairs" page could have shown the last visitor's repairs to the next one for up to a day. He moved /api/* to its own behavior with CachingDisabled plus AllViewerExceptHostHeader (the API ran on API Gateway) and kept caching only for images and scripts. "So it worked and was wrong," Jake said. "The worst kind," Ethan said.
Request collapsing: the second way one user's response reaches another
Even with short TTLs, there is one more way a shared response can happen. When several requests for the same object arrive at an edge location while the first one is still waiting on your origin, CloudFront pauses the extra requests and answers all of them with the first response. That is request collapsing, and it saves your origin from a stampede when a popular object expires. In CloudFront logs, the first request shows as a Miss and the collapsed ones as Hits.
The catch is the condition: CloudFront collapses requests that share a cache key. If your per-user API is on a behavior whose key is only the path, two signed-in users calling /api/me at the same moment have the same key, and the second can receive the first one's answer, even though nothing was "cached" in the usual sense. You prevent it in one of two ways:
- Use the managed
CachingDisabledpolicy on that behavior; it prevents both caching and request collapsing. - Or set the behavior's minimum TTL to 0 and have the origin send
Cache-Control: private,no-store,no-cache,max-age=0ors-maxage=0on those responses.
Putting Authorization or the session cookie in the cache key also stops it, because each user then has a different key. "I thought short cache times were enough," Jake said when Ethan showed him this part. "Short is not the same as none," Ethan said.
The visitor's real IP address: X-Forwarded-For and CloudFront-Viewer-Address
Behind CloudFront, the TCP connection to your origin comes from a CloudFront server, so the origin's own logs show CloudFront's addresses. The viewer's address travels in headers:
X-Forwarded-For: CloudFront adds it with the viewer's IP taken from the connection. If the viewer's request already had one, CloudFront appends the address it saw to the end. So withX-Forwarded-For: 192.0.2.4,192.0.2.3from the viewer and a connection from192.0.2.2, the origin receives192.0.2.4,192.0.2.3,192.0.2.2. The addresses before the last one were supplied by the client and can be faked; the one CloudFront appended is the one it saw.CloudFront-Viewer-Address: the viewer's IP and source port as CloudFront saw them, for example198.51.100.10:46532. Add it with an origin request policy (it can be forwarded but not used in a cache key).
If an ALB sits between CloudFront and your application, the ALB appends CloudFront's address too, so read from the right end of the list with that in mind, or use CloudFront-Viewer-Address, which nothing else rewrites. Frameworks have settings for trusting proxy headers (in Spring Boot, the "use forward headers" style options; in Express, trust proxy); configure them to trust only the hops you actually run.
Country, device and protocol headers CloudFront can add
CloudFront can add headers that describe the viewer, which is how you build country pricing, mobile layouts or "redirect HTTP to HTTPS" logic at the origin without client-side guessing. The main families:
| Header | Tells the origin | Cache key allowed? |
|---|---|---|
CloudFront-Viewer-Country, -Country-Region, -City, -Postal-Code, -Time-Zone, -Latitude, -Longitude | Where the viewer is (some IPs cannot be placed precisely) | Yes |
CloudFront-Is-Mobile-Viewer, -Is-Tablet-Viewer, -Is-Desktop-Viewer, -Is-SmartTV-Viewer, -Is-IOS-Viewer, -Is-Android-Viewer | The device type | Yes |
CloudFront-Forwarded-Proto | Whether the viewer used HTTP or HTTPS | Yes |
CloudFront-Viewer-Address, CloudFront-Viewer-ASN | Viewer IP and port; the viewer's network (ASN) | No, origin request policy only |
CloudFront-Viewer-TLS, CloudFront-Viewer-Http-Version | TLS details (HTTPS requests only) and HTTP version | TLS headers: origin request policy only |
The location family goes further than the table shows. CloudFront-Viewer-Country-Name carries the full country name, CloudFront-Viewer-Country-Region a code of up to three characters for the first-level region (a US state, for example) and -Country-Region-Name its name, CloudFront-Viewer-Time-Zone the IANA time zone such as America/Los_Angeles, and CloudFront-Viewer-Metro-Code a metro code that appears only for viewers in the United States. City, metro code and postal code are not available for every IP address, so your code must handle a missing header.
For bot and abuse detection there are request fingerprint headers: CloudFront-Viewer-Header-Order lists the viewer's header names in the order they were sent, separated by colons (truncated after 7,680 characters), CloudFront-Viewer-Header-Count gives the total number of headers, and CloudFront-Viewer-JA3-Fingerprint and CloudFront-Viewer-JA4-Fingerprint carry TLS client fingerprints. A real browser sends a predictable order and fingerprint; a script pretending to be one often does not. Forward these through an origin request policy only, since they have no business in a cache key.
For Jake's price page, the country decides the content, so CloudFront-Viewer-Country went into the cache key: one cached copy per country instead of one per visitor. Our WAF geo restriction guide covers blocking by country rather than personalizing by it.
CORS through CloudFront: request headers in, response headers out
CORS needs two things to survive CloudFront: the browser's CORS request headers must reach the origin, and the origin's Access-Control-Allow-* response headers must reach the browser, with the cache keeping different origins apart. For an S3 bucket with a CORS configuration, attach CORS-S3Origin, which forwards Origin, Access-Control-Request-Headers and Access-Control-Request-Method. For a custom origin, CORS-CustomOrigin forwards Origin. If responses vary by origin, include Origin in the cache key too, or one site's allowed origin will be cached for another. Allow the OPTIONS method on the behavior, and decide whether OPTIONS responses should be cached.
For an S3 bucket, the exact setup depends on whether you want CloudFront to cache the browser's preflight (OPTIONS) answers:
- Put the CORS configuration on the bucket first; CloudFront only passes along what S3 answers. List every site that calls the bucket in
AllowedOrigins. - On the behavior, set allowed methods to GET, HEAD, OPTIONS, and under cached methods tick OPTIONS if you want preflights cached.
- If OPTIONS is cached, forward
Origin,Access-Control-Request-HeadersandAccess-Control-Request-Method, which is exactly what the managed CORS-S3Origin policy does. If OPTIONS is not cached, forwardingOriginplus whatever else your origin requires is enough. - If more than one site calls the bucket, add
Originto the cache key with a custom cache policy, so each site gets its own cachedAccess-Control-Allow-Originvalue. - Invalidate the affected paths and test from the calling site with the browser's developer tools open; the response must show the right
Access-Control-Allow-Origin.
For a custom origin such as an ALB or API Gateway, forward Origin along with any other headers your application reads; if it answers preflights itself, the AllViewer and AllViewerExceptHostHeader policies already include everything.
Alternatively, let CloudFront add the CORS headers itself with a response headers policy: the managed SimpleCORS, CORS-With-Preflight, SecurityHeadersPolicy and CORS-and-SecurityHeadersPolicy policies add Access-Control-Allow-Origin and related headers, or HSTS, X-Content-Type-Options and other security headers, without touching your origin. A custom response headers policy can add a Content-Security-Policy too. Our S3 CORS guide covers the bucket side.
Adding your own custom headers to origin requests
Separately from forwarding viewer headers, an origin can be given custom headers that CloudFront adds to every request it sends there (in the origin's settings, "Add custom header"). Common uses: a secret header your origin requires so nobody can bypass CloudFront and reach it directly; a header identifying which distribution a request came from; or always adding Origin for viewers that do not send it. Limits: up to 30 custom headers per origin, names up to 256 characters, values up to 1,783 characters, and 10,240 characters for all names and values combined.
To lock an Application Load Balancer so it only answers requests that came through your distribution:
- Generate a long random value (for example with
openssl rand -hex 32) and pick a header name such asX-Origin-Verify. - In the CloudFront console, open the distribution's Origins tab, edit the ALB origin, and under Add custom header enter the name and the value. Save and wait for the deployment.
- On the ALB listener, add a rule with the condition HTTP header
X-Origin-Verifyequal to your value, forwarding to your target group. - Change the listener's default action to Return fixed response with status 403, so any request without the header is refused.
- Test both ways: through CloudFront the site works; straight to the ALB's DNS name it returns 403. Rotate the value now and then by adding the new value to the rule, updating CloudFront, then removing the old one.
For network-level protection as well, allow only the AWS-managed prefix list com.amazonaws.global.cloudfront.origin-facing in the ALB's security group. The secret header stops other CloudFront distributions from reaching you; the prefix list stops everyone else. Our load balancer guide covers where an ALB fits.
CloudFront refuses to add some headers this way, including Cache-Control, Connection, Content-Length, Cookie, Host, the If-* conditional headers, Pragma, Proxy-Authorization, Range, Transfer-Encoding, Upgrade, Via, X-Real-Ip, and anything starting with X-Amz- or X-Edge-. And do not list a header in an origin request policy that is also set as a custom origin header; the custom one is the one meant to win.
Header size limits and policy quotas
"CloudFront max header size" questions usually come from very large cookies or tokens. The numbers that matter:
- A request (or origin response), including headers and query strings but not the body, can be up to 32,768 bytes; a URL up to 8,192 bytes.
- A cache policy or an origin request policy can name up to 10 headers, 10 cookies and 10 query strings each (quota increases are available), with at most 1,024 characters of names combined.
- An account can have 20 custom origin request policies and 20 custom response headers policies (managed ones do not count); one origin request policy can be attached to up to 100 distributions.
Going over the size limits produces two status codes people rarely recognize. A URL longer than 8,192 bytes returns 414 (URI Too Long). A request too large because of its headers returns 494, a CloudFront-specific code. In both cases CloudFront then closes the connection to the viewer. A 494 almost always means cookies have piled up on your domain (analytics, A/B testing and chat tools each add their own); clearing them fixes one visitor, and trimming what your site sets fixes everyone.
The 10-header limit is the usual reason teams reach for "all viewer headers" instead of listing names: when you need more than ten, forwarding everything through AllViewer is allowed, while a cache policy can never include all headers (it would make the cache useless).
Why every header in the cache key costs you cache hits
The cache key is the identity of a cached object. Two requests share a cached copy only when everything in the key matches. Add Accept-Language and you may hold dozens of copies of every page, one per language string browsers send; add User-Agent and you hold thousands, because nearly every browser version sends a different one. Each extra copy means another trip to your origin before it is cached. That is why the rule is to include only values that really change the response, and to send everything else through the origin request policy instead.
Three details decide how many copies you get:
- Header name case does not matter, value case does.
Product: Acmeandproduct: Acmeshare one copy;Product: AcmeandProduct: acmeare two copies. If your front end sends inconsistent case, normalize it before it reaches CloudFront. - Header order does not matter.
A:1,B:2andB:2,A:1are the same key, as long as the values match. - Prefer a summary header over the raw one.
CloudFront-Is-Mobile-Viewerhas two values,User-Agenthas thousands.CloudFront-Viewer-Countryhas about 250 values, the raw IP has billions.
Forwarding headers through an origin request policy has no effect on these numbers. That is the whole point of separating the two policies: your origin can see the viewer's full User-Agent for analytics while the cache keeps one copy per page. Your CloudFront reports show the cache hit ratio per distribution; check it before and after any cache policy change so you see what the change cost.
Legacy cache settings: what changed and how to move off them
Older distributions, and older answers online, use legacy cache settings: on the behavior, "Cache based on selected request headers" with a whitelist, plus separate cookie and query string forwarding options. In that model, forwarding a header always also cached on it, so you could not send a header without splitting the cache, and "forward all headers" disabled caching. Policies replaced it. To move a behavior over, note the headers, cookies and query strings it forwards, then choose a cache policy for the ones that change the response and an origin request policy for the rest; managed policies cover most cases. Switch one behavior at a time and watch the cache hit ratio in the distribution's reports.
Setting it up: console, CLI and Terraform
- In the CloudFront console, open the distribution, choose Behaviors, select the behavior (or create one for a path such as
/api/*), and choose Edit. - Under Cache key and origin requests, choose Cache policy and origin request policy (recommended).
- Pick the cache policy (for an API,
CachingDisabled) and the origin request policy (AllViewer, orAllViewerExceptHostHeaderfor API Gateway and Lambda function URLs). - Allow the HTTP methods the API needs (GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE) and save. Changes take a few minutes to deploy.
In infrastructure code, use the policy IDs from the tables above, or look them up by name so code never hard-codes an ID. Terraform has data sources for exactly this:
data "aws_cloudfront_cache_policy" "disabled" {
name = "Managed-CachingDisabled"
}
data "aws_cloudfront_origin_request_policy" "all_viewer_except_host" {
name = "Managed-AllViewerExceptHostHeader"
}
# inside the aws_cloudfront_distribution, in ordered_cache_behavior { path_pattern = "/api/*" ... }
cache_policy_id = data.aws_cloudfront_cache_policy.disabled.id
origin_request_policy_id = data.aws_cloudfront_origin_request_policy.all_viewer_except_host.id
With the AWS CLI, the same two IDs go into the behavior's CachePolicyId and OriginRequestPolicyId fields in the distribution config you send to update-distribution (with the current ETag in --if-match, as our distribution guide explains).
To see the managed policies and their IDs from the command line, list them by type. This is also how you confirm an ID before pasting it into a template:
aws cloudfront list-origin-request-policies --type managed \
--query "OriginRequestPolicyList.Items[].OriginRequestPolicy.[Id,OriginRequestPolicyConfig.Name]" \
--output table
aws cloudfront list-cache-policies --type managed \
--query "CachePolicyList.Items[].CachePolicy.[Id,CachePolicyConfig.Name]" \
--output table
Swap --type managed for --type custom to see the policies your account created, which is where the 20-policy quota is counted.
Here is the shape Ethan gave Jake's distribution in the end, in precedence order (CloudFront checks the list from the top, uses the first path pattern that matches, and falls back to the default behavior last):
/api/*to the API Gateway origin: CachingDisabled with AllViewerExceptHostHeader. Every header, cookie and query string reaches the API, nothing is cached, nothing is collapsed, and API Gateway sees its own Host./prices*to the ALB origin: a custom cache policy withCloudFront-Viewer-Countryin the key and a 10-minute default TTL, with no origin request policy. One cached page per country.- Default
*to the S3 bucket: CachingOptimized with no origin request policy. Images, scripts and styles cached for a day, and the bucket never sees a viewer header it does not need.
That layout fits most small business sites. Static files get the cheapest, fastest path, per-country or per-language pages get a narrow cache key, and anything with a signed-in user goes straight through.
Prove what your origin actually receives
When you compare, remember the headers CloudFront adds on its own, so you do not chase them as bugs. Every origin request carries X-Amz-Cf-Id and a Via header naming CloudFront, and Connection becomes Keep-Alive. When a cached object has expired, CloudFront revalidates it by adding If-None-Match with the stored ETag, If-Modified-Since with the stored Last-Modified date, or both; if nothing changed, your origin can answer 304 Not Modified and CloudFront keeps serving its copy. Those conditional headers in your logs mean caching is working, not that a viewer sent them.
Guessing wastes hours; look instead. The quickest test is a tiny endpoint on the origin that returns the request headers it received (many frameworks can do this in a few lines), placed behind the same behavior. Request it through CloudFront and directly, and compare. Without an echo endpoint, the origin's access logs, configured to log the headers you care about, show the same thing. Also watch the X-Cache response header in the browser's developer tools: "Miss from cloudfront" means the request reached the origin; "Hit from cloudfront" means it did not, so header changes will not show until the cached copy expires or you invalidate it (our cache guide covers invalidations).
CloudFront headers still not arriving? Troubleshooting by symptom
The origin never sees query strings or cookies
The behavior has a cache policy that excludes them and no origin request policy. Attach AllViewer (or a custom policy naming them), or add them to the cache key if they change the response.
API Gateway returns 403 through CloudFront but works directly
The viewer's Host header is being forwarded, often by AllViewer. Use AllViewerExceptHostHeader so CloudFront sends API Gateway's own domain.
Signed-in requests fail with 401, but POST works
CloudFront strips Authorization on GET and HEAD unless a policy forwards it. Forward it, and either include it in the cache key or use CachingDisabled on that path.
Users see each other's data
A per-user response was cached without the user's identity in the cache key. Switch that path to CachingDisabled, invalidate it, and only cache per-user content with Authorization or the session cookie in the key.
502 after starting to forward the Host header
CloudFront now connects to the origin using your website's domain, and the origin's certificate does not cover it. Add a certificate for that domain on the origin, or stop forwarding Host.
The origin logs show only Amazon IP addresses
Expected: the connection comes from CloudFront. Read the viewer's address from the last trusted entry of X-Forwarded-For, or add CloudFront-Viewer-Address in an origin request policy.
CloudFront-Viewer-Country never arrives
CloudFront adds it only when a cache policy or origin request policy includes it. Add it to the cache key if the content depends on the country, otherwise to the origin request policy.
Changes to the policy have no effect
The response is still served from cache, or the change is still deploying. Check X-Cache for "Hit from cloudfront", wait for the deployment to finish, and invalidate the paths you are testing.
Redirect loop after moving the site behind CloudFront
Your application checks X-Forwarded-Proto to decide whether the request was HTTPS, and CloudFront removes that header. Add CloudFront-Forwarded-Proto to a policy and read it instead, or let CloudFront handle the redirect with the behavior's "Redirect HTTP to HTTPS" setting.
Some visitors get a 494 error
Their request headers, usually cookies, passed 32,768 bytes. Ask them to clear cookies for your domain, then cut down the cookies your site and its scripts set.
"The request could not be satisfied" errors after the change
The origin is rejecting what CloudFront now sends, or the behavior allows fewer methods than the request uses. Our CloudFront 403 guide walks through each cause.
CloudFront forwarding headers: frequently asked questions
Why is CloudFront not forwarding headers to my origin?
With cache policies, CloudFront sends only the path, body and its own headers unless a policy includes more. Attach an origin request policy such as AllViewer, or add the header to the cache policy if it changes the response.
What is the difference between a cache policy and an origin request policy?
A cache policy sets the cache key and TTLs, and everything in the key is also forwarded. An origin request policy forwards extra headers, cookies and query strings to the origin without adding them to the cache key.
How do I forward all headers in CloudFront?
Attach the managed AllViewer origin request policy to the behavior (ID 216adef6-5c7f-47e4-b989-5492eafa07d3). A cache policy cannot include all headers; pair AllViewer with CachingDisabled for APIs.
How do I forward the Host header in CloudFront?
Use AllViewer or HostHeaderOnly on custom origins that route by domain. Never forward it to API Gateway or Lambda function URLs, and it cannot be forwarded to Amazon S3 origins.
How do I forward the Authorization header through CloudFront?
Add it to the cache key with a cache policy, add it to an origin request policy, or use AllViewer. If it is not in the cache key, use CachingDisabled so one user's response is never served to another.
What is AllViewerExceptHostHeader for?
Origins that expect their own domain in Host: API Gateway, Lambda function URLs and Lambda Web Functions. It forwards everything else from the viewer and lets CloudFront set Host to the origin's domain.
How do I get the client IP address behind CloudFront?
Read X-Forwarded-For, where CloudFront appends the address it saw, or add CloudFront-Viewer-Address (IP and port) with an origin request policy. Treat earlier X-Forwarded-For entries as client-supplied.
What is the CloudFront-Forwarded-Proto header?
A CloudFront header that tells the origin whether the viewer used HTTP or HTTPS. Add it through a cache or origin request policy when the origin needs to redirect or build links by protocol.
What is the maximum header size in CloudFront?
A request, including headers and query strings but not the body, can be up to 32,768 bytes, and a URL up to 8,192 bytes. Policies can name up to 10 headers each by default.
What is a CloudFront response headers policy?
A policy that makes CloudFront add or remove response headers, such as CORS headers or security headers like HSTS and Content-Security-Policy, without changing the origin. Managed versions include SimpleCORS and SecurityHeadersPolicy.
What is the managed CachingDisabled policy ID?
4135ea2d-6df8-44a3-9df3-4b5a84be39ad. CachingOptimized is 658327ea-f89d-4fab-a63d-7e88639e58f6. In Terraform, look them up by name with the aws_cloudfront_cache_policy data source.
How do I forward cookies through CloudFront?
Include them in an origin request policy, all or by name, or in the cache key if they change the response. A session cookie on a cached path must be in the cache key, or the path must not be cached.
Does CloudFront forward query strings by default?
No. With cache policies, query strings reach the origin only if the cache policy or origin request policy includes them. With legacy settings, query string forwarding was a separate option on the behavior.
Can I add a secret header so only CloudFront can reach my origin?
Yes. Add a custom origin header in the origin settings and have the origin reject requests without it. Up to 30 custom headers per origin are allowed.
Why does my origin see User-Agent as Amazon CloudFront?
CloudFront replaces the viewer's User-Agent with "Amazon CloudFront" unless a policy forwards the original. Use UserAgentRefererHeaders or AllViewer, or the device-type headers if you only need the device class.
What are legacy cache settings in CloudFront?
The older per-behavior options for forwarding headers, cookies and query strings, where forwarding always meant caching on the value. Cache policies and origin request policies replaced them; move one behavior at a time.
For readers preparing for an AWS certification
We have been covering AWS certifications alongside our fixes, so here is a short extra for readers studying for one. If you only came for the fix, you already have it; feel free to skip this part.
Exams that test this topic: Solutions Architect – Associate (SAA-C03) • Developer – Associate (DVA-C03 from December 1, 2026) • CloudOps Engineer – Associate (SOA-C03) • Security – Specialty (SCS-C03).
Here are the exam-style questions this topic tends to raise, with the reasoning you need to answer them:
1. An API behind CloudFront needs every viewer header, cookie and query string, and responses must never be cached. Which managed policies do you attach?
The CachingDisabled cache policy with the AllViewer origin request policy (AllViewerExceptHostHeader if the origin is API Gateway or a Lambda function URL).
2. Signed-in GET requests through CloudFront fail with 401, but POST requests work. Why?
CloudFront removes the Authorization header from GET and HEAD requests unless a cache policy or origin request policy includes it.
3. A team forwards Authorization with an origin request policy on a cached behavior. What is the risk?
Authorization is not in the cache key, so CloudFront can serve one user's cached response to another user; include it in the cache key or use CachingDisabled.
4. API Gateway returns 403 only when called through CloudFront. Which origin request policy fixes it?
AllViewerExceptHostHeader, so the viewer's Host header is not forwarded and API Gateway receives its own domain name.
5. The application needs each visitor's IP address, but logs show only CloudFront addresses. What should it read?
The X-Forwarded-For header, where CloudFront appends the address it saw, or the CloudFront-Viewer-Address header added through an origin request policy.
6. How do you make sure an ALB answers only requests that came through your CloudFront distribution?
Add a secret custom origin header in CloudFront and an ALB listener rule that forwards only requests with that header, and allow only the CloudFront origin-facing managed prefix list in the security group.
Exam codes and dates change often. Our AWS certification changes page keeps track of what is current.
The booking site now has three behaviors: images and scripts cached for a day, the price page cached per country, and the API uncached with every header passed through except Host. Sign-ins work, prices match the visitor's country, and the logs show real addresses again. Jake asked whether all that needed three behaviors. "Three kinds of content," Ethan said, "three sets of rules. That is the whole trick."
📌 If you keep one line from this page
CloudFront forwards only what a policy names: use an origin request policy to send headers without caching on them, a cache policy for headers that change the response, AllViewerExceptHostHeader for API Gateway, and never forward Authorization on a cached path unless it is in the cache key.
AllViewer 216adef6-... · CachingDisabled 4135ea2d-... · 32,768-byte request limit · 10 headers per policy.
Revision note. Written October 22, 2026, from the CloudFront Developer Guide's policy, header and quota pages of that date. "Trust, but verify," the proverb says; with headers, verify with an echo endpoint before you trust a policy.