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

Logeshwaran
—

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.

⚡ Quick Answer

• Why headers vanish: by default only the path, body and CloudFront's own headers reach the origin. What arrives by default.

• Forward without caching on it: attach an origin request policy (managed AllViewer, ID 216adef6-5c7f-47e4-b989-5492eafa07d3). Managed policies.

• API Gateway or Lambda function URL origin: use AllViewerExceptHostHeader, never forward the viewer's Host. Host header.

• Authorization: forward it only with it in the cache key, or with CachingDisabled. The leak to avoid.

• Real visitor IP: the first address in X-Forwarded-For, or add CloudFront-Viewer-Address. Client IP.

Country and device headers · CORS · Custom headers · Header size limits · Troubleshooting.

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.

🧭 NEW HERE? READ THESE FIRST

New to CloudFront, API Gateway or load balancers? These five pages give you the basics this guide builds on:

💡 Open your distribution's Behaviors tab before you start; every fix below is an edit to one behavior.

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 policyOrigin request policy
DecidesThe 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 forwardedYes, that is its whole purpose
Effect on cache hit ratioEvery value added splits the cache into more copiesNone
Also setsTTLs (minimum, default, maximum) and compressionNothing else
Required?Yes, one per behaviorOptional (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 with Amazon 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.

HeaderIf no policy includes itCan be in the cache key?
Accept, Accept-Charset, Accept-Language, RefererRemovedYes
Accept-EncodingA normalized version is forwarded when the value contains gzip or brYes
AuthorizationRemoved on GET and HEAD (and on OPTIONS when OPTIONS is cached); kept on DELETE, PATCH, POST, PUTYes
CookieRemoved unless cookies are forwardedThrough the cookie settings, not as a header
HostSet to the origin's domain nameCustom origins yes; S3 and MediaStore no
User-AgentReplaced with Amazon CloudFrontYes, but not recommended
ConnectionReplaced with Connection: Keep-AliveNo
Range, If-Match, If-None-Match, If-Modified-Since, If-Range, If-Unmodified-SinceForwardedYes
Cache-Control, Content-Length, Pragma, Max-Forwards, Transfer-EncodingForwardedNo
UpgradeRemoved, except on WebSocket connectionsNo
X-Forwarded-Proto, X-Real-IP, Expect, Proxy-Authorization, TE, Trailer, X-Edge-*RemovedNo (Expect: yes)
X-HTTP-Method-OverrideRemovedYes
X-Forwarded-ForForwarded, with the viewer's IP appendedYes
X-Amz-Cf-IdAdded 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 policyForwardsUse forID
AllViewerAll viewer headers, cookies and query stringsALB, EC2 or container APIs that need everything216adef6-5c7f-47e4-b989-5492eafa07d3
AllViewerExceptHostHeaderAll viewer values except Host, plus protocol, HTTP and TLS version, device and location headersAPI Gateway, Lambda function URLs, Lambda Web Functionsb689b0a8-53d0-40ab-baf2-68738e2966ac
AllViewerAndCloudFrontHeaders-2022-06All viewer values plus every CloudFront header released through June 2022Origins that want country, device and protocol information too33f36d7e-f396-46d9-90e0-52428a34d9dc
CORS-S3OriginOrigin, Access-Control-Request-Headers, Access-Control-Request-MethodS3 buckets with a CORS configuration88a5eaf4-2fd4-4709-b370-b4c650ea3fcf
CORS-CustomOriginOriginCustom origins that answer CORS themselves59781a5b-3903-41f3-afcb-af62929ccde1
HostHeaderOnlyOnly HostOrigins that route by domain name (virtual hosts, ALB host rules)bf0718e1-ba1e-49d1-88b1-f726733018ae
UserAgentRefererHeadersOnly User-Agent and RefererAnalytics or bot rules at the originpick 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 policyCache key and TTLsUse forID
CachingOptimizedPath plus normalized Accept-Encoding only; TTL 1 s min, 24 h default, 365 days maxStatic files658327ea-f89d-4fab-a63d-7e88639e58f6
CachingDisabledNothing cached; all TTLs 0APIs and per-user pages4135ea2d-6df8-44a3-9df3-4b5a84be39ad
CachingOptimizedForUncompressedObjectsLike CachingOptimized, compression offAlready-compressed mediab2884449-e4de-46a7-ac36-70bc7f1ddd6d
UseOriginCacheControlHeadersHost, Origin, the method-override headers and all cookies; TTL 0 default, up to 365 days from the origin's Cache-ControlDynamic sites whose origin sets Cache-Control83da9c7e-98b4-4e11-a168-04f0df8e2c65
UseOriginCacheControlHeaders-QueryStringsThe same, plus all query stringsThe same, when query strings change the page4cc15a8a-d715-48a4-82b8-cc0b614638fe
AmplifyAuthorization, CloudFront-Viewer-Country, Host, all cookies and query strings; TTL 2 s default, 10 min maxAWS Amplify web apps2e54312d-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 contentCache policyOrigin request policy
S3 bucket, static website filesCachingOptimizedNone
S3 bucket used by another site's JavaScript (CORS)CachingOptimized, or a custom policy with Origin in the keyCORS-S3Origin
API Gateway REST or HTTP APICachingDisabledAllViewerExceptHostHeader
Lambda function URLCachingDisabledAllViewerExceptHostHeader
ALB, EC2 or ECS API with sign-inCachingDisabledAllViewer
ALB, EC2 or ECS website (WordPress and similar) that sets Cache-ControlUseOriginCacheControlHeaders(-QueryStrings)AllViewer
AWS Amplify web appAmplifyAllViewerExceptHostHeader

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. AllViewer or HostHeaderOnly do 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:

  1. Add Authorization to the cache key in a custom cache policy. Each token gets its own cached copy, and the header is forwarded automatically.
  2. Add Authorization individually to an origin request policy.
  3. 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:

  1. Use the managed CachingDisabled policy on that behavior; it prevents both caching and request collapsing.
  2. Or set the behavior's minimum TTL to 0 and have the origin send Cache-Control: private, no-store, no-cache, max-age=0 or s-maxage=0 on 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 with X-Forwarded-For: 192.0.2.4,192.0.2.3 from the viewer and a connection from 192.0.2.2, the origin receives 192.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 example 198.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:

HeaderTells the originCache key allowed?
CloudFront-Viewer-Country, -Country-Region, -City, -Postal-Code, -Time-Zone, -Latitude, -LongitudeWhere 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-ViewerThe device typeYes
CloudFront-Forwarded-ProtoWhether the viewer used HTTP or HTTPSYes
CloudFront-Viewer-Address, CloudFront-Viewer-ASNViewer IP and port; the viewer's network (ASN)No, origin request policy only
CloudFront-Viewer-TLS, CloudFront-Viewer-Http-VersionTLS details (HTTPS requests only) and HTTP versionTLS 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:

  1. Put the CORS configuration on the bucket first; CloudFront only passes along what S3 answers. List every site that calls the bucket in AllowedOrigins.
  2. On the behavior, set allowed methods to GET, HEAD, OPTIONS, and under cached methods tick OPTIONS if you want preflights cached.
  3. If OPTIONS is cached, forward Origin, Access-Control-Request-Headers and Access-Control-Request-Method, which is exactly what the managed CORS-S3Origin policy does. If OPTIONS is not cached, forwarding Origin plus whatever else your origin requires is enough.
  4. If more than one site calls the bucket, add Origin to the cache key with a custom cache policy, so each site gets its own cached Access-Control-Allow-Origin value.
  5. 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:

  1. Generate a long random value (for example with openssl rand -hex 32) and pick a header name such as X-Origin-Verify.
  2. 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.
  3. On the ALB listener, add a rule with the condition HTTP header X-Origin-Verify equal to your value, forwarding to your target group.
  4. Change the listener's default action to Return fixed response with status 403, so any request without the header is refused.
  5. 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: Acme and product: Acme share one copy; Product: Acme and Product: acme are two copies. If your front end sends inconsistent case, normalize it before it reaches CloudFront.
  • Header order does not matter. A:1,B:2 and B:2,A:1 are the same key, as long as the values match.
  • Prefer a summary header over the raw one. CloudFront-Is-Mobile-Viewer has two values, User-Agent has thousands. CloudFront-Viewer-Country has 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

  1. In the CloudFront console, open the distribution, choose Behaviors, select the behavior (or create one for a path such as /api/*), and choose Edit.
  2. Under Cache key and origin requests, choose Cache policy and origin request policy (recommended).
  3. Pick the cache policy (for an API, CachingDisabled) and the origin request policy (AllViewer, or AllViewerExceptHostHeader for API Gateway and Lambda function URLs).
  4. 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):

  1. /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.
  2. /prices* to the ALB origin: a custom cache policy with CloudFront-Viewer-Country in the key and a 10-minute default TTL, with no origin request policy. One cached page per country.
  3. 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.

πŸ“š ALSO READ

Where to go next, from CloudFront's error pages to the cache and CORS fixes:

📌 Bookmark this; the managed policy table with IDs is the part you will come back for.

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.

Related