
A Complete Guide to AWS CloudFront Origin Request Policies

If your origin needs data the cache key shouldn't include, an origin request policy is the right tool. It controls what CloudFront forwards to your backend on a cache miss, separately from what makes a request "unique" for caching. Use a managed policy unless you have a specific reason not to — AWS ships eight of them, and most distributions can pick one off the shelf.
The short version, before the rest of the article:
| Managed policy | Use when your origin is |
|---|---|
AllViewer | A backend that needs every viewer header, cookie, and query string |
AllViewerAndCloudFrontHeaders-2022-06 | The same, plus geolocation, device-type, or TLS metadata |
AllViewerExceptHostHeader | API Gateway or a Lambda function URL |
CORS-S3Origin | An S3 bucket serving cross-origin assets |
CORS-CustomOrigin | A non-S3 web server handling CORS |
HostHeaderOnly | A backend that needs only the original Host header |
UserAgentRefererHeaders | Doing analytics, hotlink protection, or basic device detection |
Elemental-MediaTailor-PersonalizedManifests | An AWS Elemental MediaTailor endpoint |
The rest of this article covers what these policies actually do, how they interact with the cache key (where most bugs live), and when to skip the managed list and write a custom one.
This is a follow-up to an earlier post that covered cache, origin request, and response policies as a trio — read that first if you want the broader context.
What an origin request policy actually does
CloudFront builds the request it sends to your origin from two sources. The first is everything in the cache key, controlled by your cache policy. The second is whatever your origin request policy adds on top. Without an origin request policy attached, the only headers your origin sees are the ones already in the cache key, plus a small set CloudFront automatically adds (Host, User-Agent, X-Amz-Cf-Id, and a few others).
That default is intentional. If a value isn't in the cache key, CloudFront treats requests with different versions of that value as the same cached object. Forwarding the value to the origin would make the response depend on something the cache doesn't see — and you'd get inconsistent results because the first response wins and gets served to everyone else. Origin request policies let you forward extra data to the origin while keeping the cache blind to it. That's only safe when the origin uses that data for things that don't change the response body: logging, analytics, downstream auth, or branching that returns the same content regardless.
I've seen teams reach for an origin request policy when what they actually needed was to add a value to the cache key. If your application reads User-Agent to decide between a mobile and a desktop response, forwarding it via an origin request policy isn't enough — the cached response is shared across both. The fix is to put User-Agent in the cache key, or to use a CloudFront-supplied header like CloudFront-Is-Mobile-Viewer, which is designed exactly for that case.
How it interacts with the cache policy
Anything in the cache key is automatically forwarded to the origin. The origin request policy can only add to that — it can't subtract. If your cache policy includes Authorization in the cache key, the origin will see it on a cache miss whether or not your origin request policy mentions it. This often surprises people debugging "why is this header reaching my origin when I didn't list it?"
There's also a hard requirement worth knowing up front: a cache behavior cannot have an origin request policy attached without also having a cache policy. AWS rejects the configuration. So in practice, every distribution that uses origin request policies pairs each one with a cache policy on the same behavior.
The working rule: put values in the cache key when the response actually varies on them. Put values in the origin request policy when the origin needs them but the response is the same regardless.
What's inside an origin request policy
Three knobs: headers, cookies, and query strings. Each one accepts a behavior, plus an optional list when the behavior calls for one.
For headers, the available behaviors are:
none— no viewer headers forwarded (cache-key headers still go through, plus the small CloudFront-added set)whitelist— only the listed headersallViewer— every viewer headerallViewerAndWhitelistCloudFront— every viewer header plus specific CloudFront headers (geolocation, device-type, TLS info)allExcept— every viewer header except a listed set
Cookies and query strings have a similar shape but a smaller set: none, all, whitelist, or allExcept. There's no allViewerAndWhitelistCloudFront equivalent because CloudFront doesn't add its own cookies or query strings.
A subtlety worth knowing before you read the managed policies below: when a policy uses allExcept to remove the viewer's Host header, CloudFront automatically replaces it with a new Host header containing the origin's domain name. That's exactly what makes AllViewerExceptHostHeader work for API Gateway — the gateway sees its own domain in the Host header and routes correctly, instead of seeing the CloudFront distribution's domain and rejecting the request.
The eight managed policies
Each managed policy has a stable ID that you reference from CloudFormation, the AWS CLI, the SDKs, or your IaC tool of choice. The IDs don't change, so it's safe to hardcode them.
AllViewer
Forwards every header, cookie, and query string from the viewer request to the origin.
ID: 216adef6-5c7f-47e4-b989-5492eafa07d3
Use this when your backend genuinely needs the full request — typically for legacy applications, complex authenticated apps, or anything where you can't easily enumerate which headers and cookies matter. The trade-off is that it forwards the viewer's Host header too, which means it doesn't work with API Gateway or Lambda function URLs (use AllViewerExceptHostHeader for those). It also forwards anything sensitive that the client happens to send, so think about whether your origin should see, for instance, a long-lived authentication cookie that some unrelated part of your app sets.
AllViewerAndCloudFrontHeaders-2022-06
Same as AllViewer plus a fixed set of CloudFront-added headers: CloudFront-Forwarded-Proto, the device-type series (-Is-Android-Viewer, -Is-Mobile-Viewer, -Is-Tablet-Viewer, etc.), the geolocation series (-Viewer-Country, -Viewer-City, -Viewer-Latitude, -Viewer-Longitude, -Viewer-Time-Zone, -Viewer-Postal-Code, -Viewer-ASN, and a few more), CloudFront-Viewer-Http-Version, and CloudFront-Viewer-TLS.
ID: 33f36d7e-f396-46d9-90e0-52428a34d9dc
The -2022-06 suffix is significant. The set of headers this policy forwards is frozen as of June 2022 — any CloudFront viewer header added after that date isn't included. AWS does this so existing distributions don't change behavior when new headers ship. If you want a newer CloudFront header in your origin requests, write a custom policy.
Use this when your origin uses CloudFront's enriched headers for personalization, analytics, or routing. Geolocation-based content selection is the common case: your backend reads CloudFront-Viewer-Country and serves a localized response, without doing GeoIP lookups itself.
AllViewerExceptHostHeader
Forwards everything except the Host header. CloudFront then injects the origin's domain name as the new Host.
ID: b689b0a8-53d0-40ab-baf2-68738e2966ac
This is the policy you almost certainly want when your origin is API Gateway, a Lambda function URL, or any AWS-managed endpoint that does virtual hosting based on the Host header. These services check Host against their own domain. If CloudFront forwards the viewer's Host (which is your CloudFront distribution's domain or your custom domain), they don't recognize it and respond with a 403.
It also includes the same set of CloudFront viewer headers as AllViewerAndCloudFrontHeaders-2022-06, which means you get geolocation and device-type metadata for free. For most API Gateway-backed CloudFront distributions, this is a one-line answer to a problem that used to require fiddling with cache behaviors and Lambda@Edge.
CORS-CustomOrigin
Forwards only the Origin header. No cookies, no query strings.
ID: 59781a5b-3903-41f3-afcb-af62929ccde1
Use this when your origin is anything other than S3 — an ALB, an EC2 instance, a containerized service, or a third-party HTTP server — and it serves cross-origin requests. The Origin header is what the server inspects to decide whether to return CORS-allowing response headers. Anything more just bloats the origin request without any CORS benefit, and risks forwarding data the origin doesn't need to see.
This policy pairs naturally with the CORS-with-preflight-and-SecurityHeadersPolicy response headers policy when your backend doesn't reliably set CORS headers itself.
CORS-S3Origin
Forwards Origin, Access-Control-Request-Headers, and Access-Control-Request-Method. No cookies, no query strings.
ID: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf
S3 is fussier about CORS than a custom server. To return the right CORS response headers, S3 needs to see all three of these request headers — not just Origin. The standard CORS forwarding policy (CORS-CustomOrigin) doesn't work because it omits the other two. If you've ever spent time debugging "CORS works locally but not through CloudFront" with an S3 origin, this is almost certainly the missing piece.
Note that this policy is for S3-as-origin scenarios where you've also configured a CORS rule on the bucket itself. Without the bucket-side CORS configuration, S3 won't return the Access-Control-Allow-* headers regardless of what gets forwarded.
HostHeaderOnly
Forwards just the Host header. Nothing else.
ID: bf0718e1-ba1e-49d1-88b1-f726733018ae
A narrow policy with a specific use case: multi-tenant origins that route based on the original viewer Host. If your backend serves multiple custom domains and uses the Host header to figure out which tenant a request belongs to, you need that header to come through unchanged. Most other managed policies either rewrite Host or don't forward enough information to make it useful.
If you're using this with anything other than a multi-tenant routing scenario, double-check whether you actually want it. For API Gateway origins, HostHeaderOnly will not work — API Gateway needs the rewritten Host, which is what AllViewerExceptHostHeader gives you instead.
UserAgentRefererHeaders
Forwards only User-Agent and Referer.
ID: acba4595-bd28-49b8-b9fe-13317c0390fa
This is the policy for cases where the origin needs to know "what kind of client is this?" or "where did the request come from?" without seeing the rest of the headers, cookies, or query strings. Two common uses: hotlink protection (block requests where Referer doesn't match an allowed list) and analytics that bucket traffic by browser or operating system.
It pairs well with a tight cache policy — typically CachingOptimized — because neither header is in the cache key. The cache stays efficient and the origin still gets enough data to do its filtering or counting.
Elemental-MediaTailor-PersonalizedManifests
Forwards Origin, Access-Control-Request-Headers, Access-Control-Request-Method, User-Agent, X-Forwarded-For, and all query strings.
ID: 775133bc-15f2-49f9-abea-afb2e0bf67d2
A specialized policy for AWS Elemental MediaTailor, which serves personalized HLS/DASH manifests for ad insertion in video streaming. MediaTailor needs the CORS triplet for browser playback, User-Agent and X-Forwarded-For for ad-decisioning logic, and the full query string because session and personalization tokens are encoded there. If you're not using MediaTailor, you don't need this policy — but if you are, it's the difference between manifests that work and manifests that don't.
Comparison: what each managed policy forwards
| Policy | Headers | Cookies | Query strings |
|---|---|---|---|
AllViewer | All viewer | All | All |
AllViewerAndCloudFrontHeaders-2022-06 | All viewer + frozen CF set | All | All |
AllViewerExceptHostHeader | All viewer except Host + CF set | All | All |
CORS-CustomOrigin | Origin | None | None |
CORS-S3Origin | Origin, Access-Control-Request-Headers, Access-Control-Request-Method | None | None |
HostHeaderOnly | Host | None | None |
UserAgentRefererHeaders | User-Agent, Referer | None | None |
Elemental-MediaTailor-PersonalizedManifests | CORS triplet + User-Agent + X-Forwarded-For | None | All |
When to write a custom origin request policy
The managed policies cover most cases. A custom policy is needed when:
- Your origin needs a specific application header (like
X-Tenant-IDorX-Request-Trace) that isn't in any managed policy - You want CloudFront viewer headers added after June 2022 — the managed
AllViewerAndCloudFrontHeaders-2022-06is frozen at that date - A specific cookie or query string needs to pass through while others stay blocked (no managed policy uses the
whitelistbehavior for cookies or query strings) - The requirements get oddly mixed — e.g., "everything except
Authorization, plus a few CloudFront geolocation headers"
Here's a Terraform example for a custom policy that forwards a tenant header, geolocation, and a session cookie, while excluding everything else:
resource "aws_cloudfront_origin_request_policy" "tenant_api" { name = "tenant-api-policy" comment = "Forwards tenant routing data and CF geolocation" headers_config { header_behavior = "whitelist" headers { items = [ "X-Tenant-ID", "CloudFront-Viewer-Country", "CloudFront-Viewer-City", ] } } cookies_config { cookie_behavior = "whitelist" cookies { items = ["session_id"] } } query_strings_config { query_string_behavior = "none" } }
A subtle gotcha worth knowing: under allViewerAndWhitelistCloudFront, the headers.items list isn't the only set of headers forwarded — every viewer header is forwarded too, and the list just adds CloudFront-specific headers on top. The behavior name is more literal than it looks. To restrict to a specific allowlist, use whitelist (as in the example above) and list both the viewer headers and any CloudFront headers you want.
To list all available origin request policies (managed and custom) from the AWS CLI:
# Returns the 8 managed policies with their IDs aws cloudfront list-origin-request-policies --type managed # Or list only your custom policies aws cloudfront list-origin-request-policies --type custom
To attach a managed policy by ID, set OriginRequestPolicyId on the relevant cache behavior in your distribution config:
aws cloudfront update-distribution \ --id E1A2B3C4D5E6F7 \ --distribution-config file://config.json
Common pitfalls
A header isn't reaching the origin in the form you expect
Check whether the same header is configured under Origin Custom Headers on the distribution. AWS specifically warns against setting the same header in both places. Origin custom headers are applied before CloudFront forwards the request, and if the header is already present in the viewer request, CloudFront overwrites the value. The origin sees one deterministic value, but it may not be the one you set in the origin request policy. Pick one mechanism per header and you avoid the ambiguity.
API Gateway returns 403 even though Host isn't in the allowlist
If you wrote a custom policy and used whitelist for headers without including Host in the allowlist, the viewer's Host header isn't forwarded — but it also isn't replaced. CloudFront's automatic Host rewrite only kicks in with the allExcept behavior when Host is in the excluded list. For API Gateway, either use AllViewerExceptHostHeader (the easiest path), or use allExcept with Host in the exclusion list.
Authorization reaches the origin even though it's not in the policy
Your cache policy is including it. Cache-key headers are automatically forwarded; the origin request policy can only add to that. Open the cache policy attached to the same cache behavior and check whether Authorization is listed under headers there. If you actually want the header to stay out of the origin requests, you'll need to remove it from the cache key — and accept the implications for cache hit ratio, since responses will no longer be keyed on it.
A CloudFront-added header isn't arriving
Two different causes look the same from the origin side. First, the header may simply not be in your policy: a bare AllViewer doesn't include any CloudFront-added headers, and AllViewerAndCloudFrontHeaders-2022-06 is frozen at the set of headers AWS released through June 2022. Anything added later — CloudFront-Viewer-JA3-Fingerprint and CloudFront-Viewer-JA4-Fingerprint for TLS fingerprinting, CloudFront-Viewer-Header-Order and CloudFront-Viewer-Header-Count for bot detection, CloudFront-Error-Uri and CloudFront-Error-Args for error-handling flows — is not in the managed -2022-06 policy. To get those, write a custom origin request policy and list them explicitly.
Second, even when the header is included, AWS notes that CloudFront-Viewer-City, CloudFront-Viewer-Metro-Code, and CloudFront-Viewer-Postal-Code may not be available for every IP address — some addresses don't geolocate with enough precision. If those fields are blank for some traffic but populated for others, the policy is fine; the geolocation data just isn't there.
Cookies aren't reaching a Lambda function URL even with AllViewerExceptHostHeader
Three things to check, in order. First, confirm the browser actually sent the cookie to your CloudFront domain — Secure, HttpOnly, SameSite=Strict, and Domain mismatches all stop browsers from including the cookie in the first place, and no policy can rescue you if the cookie never crossed the wire. Second, confirm the policy you chose includes cookies: AllViewerExceptHostHeader does forward all cookies, but if you wrote a custom policy and set cookie_behavior = "none", you'll see no cookies at the origin regardless of what was in the request. Third, when you do receive the request at the function, remember that Lambda function URLs use the API Gateway v2.0 payload format — cookies arrive in the event's cookies array, not in headers["Cookie"]. Code that reads the Cookie header directly will appear to receive nothing even when the cookies are present in the event.
If the issue is specific to cross-origin browser requests, the function URL's CORS configuration matters too — specifically the AllowCredentials setting, which has to be true for credentialed requests to be allowed at all. But that's only one of three things that need to align: the cookie has to permit cross-origin sending (typically SameSite=None; Secure), the client-side fetch has to opt into credentials (credentials: 'include'), and the function URL has to allow credentials. Miss any one and the cookie won't reach the function — and none of that is something CloudFront can fix from the edge.
Picking the right policy
A short decision tree:
- Is your origin API Gateway or a Lambda function URL? →
AllViewerExceptHostHeader - Is your origin S3 and you need CORS? →
CORS-S3Origin - Is your origin a custom server (ALB, EC2, ECS) with CORS? →
CORS-CustomOrigin - Are you using AWS Elemental MediaTailor? →
Elemental-MediaTailor-PersonalizedManifests - Do you only need to identify the client? →
UserAgentRefererHeaders - Are you running multi-tenant routing on
Host? →HostHeaderOnly - Does your origin need everything? →
AllViewer, orAllViewerAndCloudFrontHeaders-2022-06if you want geolocation too - None of the above? → Write a custom policy
If none of the managed policies match exactly but you're close, custom policies are cheap to create and easy to version. There's no API quota concern for normal use, and you can test changes by attaching the new policy to a non-production cache behavior first.
The next post in this series will go through CloudFront response headers policies in the same depth — what each managed policy adds, when the browser actually honors it, and where custom policies are worth the maintenance cost.
Need help optimizing your CDN?
If you are struggling with complex CloudFront configurations or cache behavior, reach out to u11d for expert engineering guidance.

Frequently Asked Questions
What is the primary purpose of an origin request policy?
An origin request policy controls which viewer headers, cookies, and query strings CloudFront forwards to your backend on a cache miss. It allows you to pass data to your origin without including that data in the cache key.
How do origin request policies interact with the cache key?
Anything in your cache key is automatically forwarded to the origin. The origin request policy can only add additional data, not subtract headers that are already part of the cache key.
Why is 'AllViewerExceptHostHeader' recommended for API Gateway?
API Gateway needs to see its own domain name in the Host header to route requests correctly. This policy removes the viewer's Host header and replaces it with the origin's domain name, preventing 403 errors.
When should I write a custom origin request policy?
You should use a custom policy if you need specific application headers, require CloudFront headers added after June 2022, or need a granular 'whitelist' of cookies or query strings that managed policies do not provide.
Can I use an origin request policy without a cache policy?
No, AWS requires that every cache behavior with an origin request policy attached must also have a cache policy. They are effectively paired for every distribution.
Why does my origin see the Authorization header when I haven't listed it?
The header is likely present in your cache policy's cache key. Since everything in the cache key is forwarded to the origin, you must remove it from the cache key if you want to prevent it from reaching the backend.





