Fetch Metadata and Isolation Policies
Name Teo Selenius Twitter Follow @TeoSelenius
Learn everything about the fetch metadata headers and how you can implement isolation policies to defend against various client-side attacks.
What are fetch metadata headers?
Fetch metadata headers are a relatively new browser security feature that you can use in your web application to protect against client-side attacks like never before.
The purpose of the headers is simple. They tell the web server about the context in which an HTTP request happened.
An example
Here's Jim. He is logged in at b.example with the cookie SessionId=123 .
Say that there's a website a.example that loads an image from b.example .
Without fetch metadata headers
Before the fetch metadata headers existed, all the webserver saw when Jim opened a.example was that someone with Jim's cookie loaded the image.
On HTTP level it might look like this:
Note that the server has no way to know whether it's a.example or b.example loading the image with Jim's session ID.
With fetch metadata headers
With fetch metadata headers supported, the browser will send more information in the request to the webserver.
On HTTP level, it might look like this:
Notice how the browser told the webserver three new things this time:
- The request originated from an image element.
- The request's mode (that this was a resource load as opposed to, e.g., navigation).
- The request originated from another website.
Actually there is a fourth thing implied in the request as well. The lack of Sec-Fetch-User header implies that the request didn't originate as a result of user interaction.
What attacks fetch metadata headers prevent?
Fetch metadata headers can help prevent pretty much any cross-site attack. These include:
Fetch metadata headers
The system consists of four HTTP request headers.
Sec-Fetch-Site
The Sec-Fetch-Site header tells the webserver if the request originated from the same origin, the same site, or a different website entirely. It can have one of the following values:
- same-origin : The request came from the same origin, that is, the host, port, and scheme were identical. You can read more about what origin means here.
- same-site : The request came from a different origin but from the same site, which is browser lingo for subdomain of the same registrable domain. For example: https://a.example.com and https://b.example.com are considered same-site (but different origin).
- cross-site : The request came from a different website completely. For example, www.google.com and www.facebook.com are considered cross-site.
- none : The request didn't originate from any website. This happens when the user opens a bookmark, types manually in the URL bar, opens a link from another program, etc.
Sec-Fetch-Mode
The Sec-Fetch-Mode tells the webserver the request's mode. It can have one of the following values:
- same-origin : The browser is making a request to the same origin. Requests using this mode will fail if the target is of a different origin.
- no-cors : The browser is making a request to another origin but doesn't expect to read the response or use any non-safelisted HTTP verbs or headers.
- cors : The browser attempts to make a CORS (Cross-Origin Resource Sharing) request. You can read more about CORS here.
- navigate : The browser is navigating from one page to another, such as when clicking a link, receiving a redirect, or opening a bookmark.
Sec-Fetch-Dest
The Sec-Fetch-Dest tells the webserver the request's destination, that is, what kind of place is waiting for the resource. In the example above, it was an <img> element loading the resource, so the destination was image .
Some of the possible values are:
- empty : When the resource is loaded via fetch() or XHR.
- document : When the resource is loaded in top-level navigation.
- image : When the resource is loaded in an <img> tag.
- worker : When the resource is loaded via new Worker(. ) .
- iframe : When the resource is loaded into an <iframe> .
The value can be any element capable of loading an external resource, so also audio , audioworklet , embed , font , frame , manifest , object , paintworklet , report , script , serviceworker , sharedworker , style , track , video , xslt , etc. are possible.
Sec-Fetch-User
The Sec-Fetch-User header tells the webserver a navigation request originated (in theory) due to user interaction, such as by clicking a link. The value is always ?1 . When navigation occurs as a result of something that browsers don't consider "user activation", this header is not sent at all.
Browser support
At the time of this writing, fetch metadata headers are supported on Chrome, Edge, and Opera. But don't worry, you can implement a policy in a fully backward-compatible way. You can check the up-to-date status here.
Implementing an isolation policy
The reason fetch metadata headers are so fantastic is that they allow us to do this.
Blocking a request on the server-side based on the client-side context is a power that we've never had before. But now we do, so let's use it and implement an isolation policy.
To create an isolation policy, you need a filter, middleware, etc., that enables you to block requests based on the HTTP request headers.
I will use NodeJS as an example, but such middleware is usually trivial to implement in any development framework.
We'll start with this skeleton:
1. Backward compatibility
Begin your policy by allowing requests that don't have the fetch metadata headers at all. Otherwise, people using browsers that don't yet support fetch metadata wouldn't be able to access your website.
2. Allow all requests from the same origin
To make your application work properly, allow all interactions from the same origin.
3. Allow requests that don't originate from another website
Sometimes requests originate from the user opening a bookmark or typing in the URL bar. In these cases, the value of Sec-Fetch-Site is none , so let's allow that.
4. Allow navigation
To enable other websites to link to your page, navigation has to be allowed. However, just allowing navigation would also allow cross-site POST requests, framing, and other things we don't necessarily want.
To be safe, verify that the HTTP method is GET and that the resource destination is document .
5. Block any other requests
If a request didn't match any rules so far, reject it.
6. Relax the policy if required
You may want to allow some cross-origin or cross-site interactions.
Allow subdomains
To allow requests from your own subdomains, let requests through that have the Sec-Fetch-Site value of same-site .
Allow framing
To enable other websites to load your page in an iframe, allow navigating GET requests when the destination is iframe .
7. Test the policy
We have now implemented a rather strict isolation policy. Here is the entire thing (I didn't allow iframes or subdomains in my example). You can fork and play with the code here.
I've created a little test page here. It tries to load an image, POST an HTML form and frame the protected website. If you open the site, you will find that all of the three will fail.
There is also a link. Clicking through the link, the page loads correctly.
Also, loading the page in Firefox or Safari works fine, so our backward compatibility seems to be in check.
8. Deploy the policy
When deploying the policy to production for the first time, it is recommended to use a logging-only approach.
The policy would remain the same, but instead of blocking requests, you log that a request would have been blocked because of X, and then you let the request through.
This way, you will quickly know if the policy would break something and if there's something you have to add before finally deploying the enforcing policy.
Conclusion
Fetch metadata headers are a remarkable browser feature that enables developers to secure web applications in a way that hasn't previously been possible.
Implementing an effective policy is simple, and it can easily be relaxed to accommodate any special needs, such as allowing framing or interactions from subdomains.
When deploying the policy to production, it is recommended to start with a logging-only approach. Then later deploy the enforcing policy when you're satisfied that it won't break anything.
Browser support is still lacking or experimental depending on the browser, but the headers can already be used in a backward compatible way.
Protect your resources from web attacks with Fetch Metadata
Prevent CSRF, XSSI, and cross-origin information leaks.

Why should you care about isolating your web resources? #
Many web applications are vulnerable to cross-origin attacks like cross-site request forgery (CSRF), cross-site script inclusion (XSSI), timing attacks, cross-origin information leaks or speculative execution side-channel (Spectre) attacks.
Fetch Metadata request headers allow you to deploy a strong defense-in-depth mechanism—a Resource Isolation Policy—to protect your application against these common cross-origin attacks.
It is common for resources exposed by a given web application to only be loaded by the application itself, and not by other websites. In such cases, deploying a Resource Isolation Policy based on Fetch Metadata request headers takes little effort, and at the same time protects the application from cross-site attacks.
Browser compatibility #
Fetch Metadata request headers are supported as of Firefox 90 and as of Chrome 76 in all Chromium-based browsers.
Background #
Many cross-site attacks are possible because the web is open by default and your application server cannot easily protect itself from communication originating from external applications. A typical cross-origin attack is cross-site request forgery (CSRF) where an attacker lures a user onto a site they control and then submits a form to the server the user is logged in to. Since the server cannot tell if the request originated from another domain (cross-site) and the browser automatically attaches cookies to cross-site requests, the server will execute the action requested by the attacker on behalf of the user.
Other cross-site attacks like cross-site script inclusion (XSSI) or cross-origin information leaks are similar in nature to CSRF and rely on loading resources from a victim application in an attacker-controlled document and leaking information about the victim applications. Since applications cannot easily distinguish trusted requests from untrusted ones, they cannot discard malicious cross-site traffic.
Gotchas
Introducing Fetch Metadata #
Fetch Metadata request headers are a new web platform security feature designed to help servers defend themselves against cross-origin attacks. By providing information about the context of an HTTP request in a set of Sec-Fetch-* headers, they allow the responding server to apply security policies before processing the request. This lets developers decide whether to accept or reject a request based on the way it was made and the context in which it will be used, making it possible to respond to only legitimate requests made by their own application.
Requests originating from sites served by your own server (same-origin) will continue to work.

Malicious cross-site requests can be rejected by the server because of the additional context in the HTTP request provided by Sec-Fetch-* headers.

Sec-Fetch-Site #
- same-origin , if the request was made by your own application (e.g. site.example )
- same-site , if the request was made by a subdomain of your site (e.g. bar.site.example )
- none , if the request was explicitly caused by a user’s interaction with the user agent (e.g. clicking on a bookmark)
- cross-site , if the request was sent by another website (e.g. evil.example )
Sec-Fetch-Mode #
Sec-Fetch-Mode indicates the mode of the request. This roughly corresponds to the type of the request and allows you to distinguish resource loads from navigation requests. For example, a destination of navigate indicates a top-level navigation request while no-cors indicates resource requests like loading an image.
Sec-Fetch-Dest #
Sec-Fetch-Dest exposes a request’s destination (e.g. if a script or an img tag caused a resource to be requested by the browser).
How to use Fetch Metadata to protect against cross-origin attacks #
The extra information these request headers provide is quite simple, but the additional context allows you to build powerful security logic on the server-side, also referred to as a Resource Isolation Policy, with just a few lines of code.
Implementing a Resource Isolation Policy #
A Resource Isolation Policy prevents your resources from being requested by external websites. Blocking such traffic mitigates common cross-site web vulnerabilities such as CSRF, XSSI, timing attacks, and cross-origin information leaks. This policy can be enabled for all endpoints of your application and will allow all resource requests coming from your own application as well as direct navigations (via an HTTP GET request). Endpoints that are supposed to be loaded in a cross-site context (e.g. endpoints loaded using CORS) can be opted out of this logic.
Step 1: Allow requests from browsers which don’t send Fetch Metadata #
Since not all browsers support Fetch Metadata, you need to allow requests that don’t set Sec-Fetch-* headers by checking for the presence of sec-fetch-site .
Caution
Step 2: Allow same-site and browser-initiated requests #
- Originate from your own application (e.g. a same-origin request where site.example requests site.example/foo.json will always be allowed).
- Originate from your subdomains.
- Are explicitly caused by a user’s interaction with the user agent (e.g. direct navigation or by clicking a bookmark, etc.).
Gotchas
Step 3: Allow simple top-level navigation and iframing #
To ensure that your site can still be linked from other sites, you have to allow simple ( HTTP GET ) top-level navigation.
Gotchas
Step 4: Opt out endpoints that are meant to serve cross-site traffic (Optional) #
- Endpoints meant to be accessed cross-origin: If your application is serving endpoints that are CORS enabled, you need to explicitly opt them out from resource isolation to ensure that cross-site requests to these endpoints are still possible.
- Public resources (e.g. images, styles, etc.): Any public and unauthenticated resources that should be loadable cross-origin from other sites can be exempted as well.
Caution
Step 5: Reject all other requests that are cross-site and not navigational #
Any other cross-site request will be rejected by this Resource Isolation Policy and thus protect your application from common cross-site attacks.
Gotchas
Example: The following code demonstrates a complete implementation of a robust Resource Isolation Policy on the server or as a middleware to deny potentially malicious cross-site resource requests, while allowing simple navigational requests:
Deploying a Resource Isolation Policy #
- Install a module like the code snippet from above to log and monitor how your site behaves and make sure the restrictions don’t affect any legitimate traffic.
- Fix potential violations by exempting legitimate cross-origin endpoints.
- Enforce the policy by dropping non-compliant requests.
Identifying and fixing policy violations #
It’s recommended that you test your policy in a side-effect free way by first enabling it in reporting mode in your server-side code. Alternatively, you can implement this logic in middleware, or in a reverse proxy which logs any violations that your policy might produce when applied to production traffic.
From our experience of rolling out a Fetch Metadata Resource Isolation Policy at Google, most applications are by default compatible with such a policy and rarely require exempting endpoints to allow cross-site traffic.
Enforcing a Resource Isolation Policy #
After you’ve checked that your policy doesn’t impact legitimate production traffic, you’re ready to enforce restrictions, guaranteeing that other sites will not be able to request your resources and protecting your users from cross-site attacks.
Sec-Fetch-Mode
Вообще говоря, это позволяет серверу различать: запросы, исходящие от пользователя, перемещающегося между HTML-страницами, и запросы на загрузку изображений и других ресурсов. Например, этот заголовок будет содержать navigate для запросов навигации верхнего уровня, в то время как no-cors используется для загрузки изображения.
| Header type | Заголовок запроса на получение метаданных |
|---|---|
| Запрещенное имя заголовка | да (префикс Sec- ) |
| Заголовок запроса с CORS-защитой | no |
Syntax
Серверы должны игнорировать этот заголовок,если он содержит любое другое значение.
Directives
Примечание. Эти директивы соответствуют значениям в Request.mode .
Запрос является запросом протокола CORS .
Запрос инициируется при навигации между HTML-документами.
Запрос является запросом без Request.mode (см. Request.mode ).
Запрос выполняется из того же источника,что и запрашиваемый ресурс.
Сделан запрос на установление соединения WebSocket .
Examples
Если пользователь щелкает ссылку страницы на другую страницу того же источника, полученный запрос будет иметь следующие заголовки (обратите внимание, что это режим navigate ):
Межсайтовый запрос, сгенерированный элементом <img> , приведет к запросу со следующими заголовками HTTP-запроса (обратите внимание, что это режим no-cors ):
Fetch Metadata Request Headers
This document defines a set of Fetch metadata request headers that aim to provide servers with enough information to make a priori decisions about whether or not to service a request based on the way it was made, and the context in which it will be used.
Status of this document
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.
This document was published by the Web Application Security Working Group as a Working Draft. This document is intended to become a W3C Recommendation.
The (archived) public mailing list public-webappsec@w3.org (see instructions) is preferred for discussion of this specification. When sending e-mail, please put the text “fetch-metadata” in the subject, preferably like this: “[fetch-metadata] …summary of comment…”
Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
1. Introduction
Interesting web applications generally end up with a large number of web-exposed endpoints that might reveal sensitive data about a user, or take action on a user’s behalf. Since users’ browsers can be easily convinced to make requests to those endpoints, and to include the users’ ambient credentials (cookies, privileged position on an intranet, etc), applications need to be very careful about the way those endpoints work in order to avoid abuse.
Being careful turns out to be hard in some cases («simple» CSRF), and practically impossible in others (cross-site search, timing attacks, etc). The latter category includes timing attacks based on the server-side processing necessary to generate certain responses, and length measurements (both via web-facing timing attacks and passive network attackers).
It would be helpful if servers could make more intelligent decisions about whether or not to respond to a given request based on the way that it’s made in order to mitigate the latter category. For example, it seems pretty unlikely that a «Transfer all my money» endpoint on a bank’s server would expect to be referenced from an img tag, and likewise unlikely that evil.com is going to be making any legitimate requests whatsoever. Ideally, the server could reject these requests a priori rather than delivering them to the application backend.
Here, we describe a mechanims by which user agents can enable this kind of decision-making by adding additional context to outgoing requests. By delivering metadata to a server in a set of fetch metadata headers, we enable applications to quickly reject requests based on testing a set of preconditions. That work can even be lifted up above the application layer (to reverse proxies, CDNs, etc) if desired.
1.1. Examples
A request generated by a picture element would result in a request containing the following HTTP request headers:
A top-level navigation from https://example.com to https://example.com/ caused by a user’s click on an in-page link would result in a request containing the following HTTP request header:
2. Fetch Metadata Headers
The following sections define several , each of which exposes an interesting request attribute to a server.
2.1. The Sec-Fetch-Dest HTTP Request Header
The HTTP request header exposes a request’s destination to a server. It is a Structured Header whose value MUST be a token. [I-D.ietf-httpbis-header-structure] Its ABNF is:
Valid Sec-Fetch-Dest values include » audio «, » audioworklet «, » document «, » embed «, » empty «, » font «, » frame «, » iframe «, » image «, » manifest «, » object «, » paintworklet «, » report «, » script «, » serviceworker «, » sharedworker «, » style «, » track «, » video «, » worker «, » xslt «.
In order to support forward-compatibility with as-yet-unknown request types, servers SHOULD ignore this header if it contains an invalid value.
Let header be a Structured Header whose value is a token.
If r ’s destination is the empty string, set header ’s value to the string » empty «. Otherwise, set header ’s value to r ’s destination.
Note: We map Fetch’s empty string destination onto an explicit » empty » token in order to simplify processing.
2.2. The Sec-Fetch-Mode HTTP Request Header
The HTTP request header exposes a request’s mode to a server. It is a Structured Header whose value is a token. [I-D.ietf-httpbis-header-structure] Its ABNF is:
Valid Sec-Fetch-Mode values include » cors «, » navigate «, » no-cors «, » same-origin «, and » websocket «. In order to support forward-compatibility with as-yet-unknown request types, servers SHOULD ignore this header if it contains an invalid value.
Let header be a Structured Header whose value is a token.
Set header ’s value to r ’s mode.
2.3. The Sec-Fetch-Site HTTP Request Header
The HTTP request header exposes the relationship between a request initiator’s origin and its target’s origin. It is a Structured Header whose value is a token. [I-D.ietf-httpbis-header-structure] Its ABNF is:
Valid Sec-Fetch-Site values include » cross-site «, » same-origin «, » same-site «, and » none «. In order to support forward-compatibility with as-yet-unknown request types, servers SHOULD ignore this header if it contains an invalid value.
Let header be a Structured Header whose value is a token.
Set header ’s value to same-origin .
If r is a navigation request that was explicitly caused by a user’s interaction with the user agent (by typing an address into the user agent directly, for example, or by clicking a bookmark, etc.), then set header ’s value to none .
Note: See § 4.3 Directly User-Initiated Requests for more detail on this somewhat poorly-defined step.
If header ’s value is not none , then for each url in r ’s url list:
Set header ’s value to cross-site .
If r ’s origin is not same site with url ’s origin, then break.
Set header ’s value to same-site .
2.4. The Sec-Fetch-User HTTP Request Header
The HTTP request header exposes whether or not a navigation request was triggered by user activation. It is a Structured Header whose value is a boolean. [I-D.ietf-httpbis-header-structure] Its ABNF is:
Note: The header is delivered only for navigation requests, and only when its value is true . It might be reasonable to expand the headers’ scope in the future to include subresource requests generally if we can spell out some use cases that would be improved by exposing that information (and if we can agree on ways to define that status for all the subresource request types we’d be interested in), but for the moment, navigation requests have clear use cases, and seem straightforward to define interoperably.
If r is not a navigation request, or if r ’s user activation is false , return.
Let header be a Structured Header whose value is a token.
Set header ’s value to true .
3. Integration with Fetch and HTML
To support Sec-Fetch-User , request has a user-activation which is false, unless otherwise populated by HTML’s process a navigate fetch algorithm.
Fetch Metadata headers are appended to outgoing requests from within Fetch’s «HTTP-network-or-cache» algorithm, using the following steps. Consult that specification for integration details [FETCH].
4. Security and Privacy Considerations
4.1. Redirects
The user agent will send a Sec-Fetch-Site header along with each request in a redirect chain. The header’s value will shift in the presence of cross-origin or cross-site redirection in order to mitigate confusion.
The algorithm to set the Sec-Fetch-Site header walks the request’s entire url list, and will send cross-site if any URL in the list is cross-site to the request’s current url, same-site only if all URLs in the list are same-site with the request’s current url, and same-origin only if all URLs in the list are same-origin with the request’s current url.
For example, if https://example.com/ requests https://example.com/redirect , the initial request’s Sec-Fetch-Site value would be same-origin . If that response redirected to https://subdomain.example.com/redirect , that request’s Sec-Fetch-Site value would be same-site (as https://subdomain.example.com/ and https://example.com/ have the same registrable domain). If that response redirected to https://example.net/redirect , that request’s Sec-Fetch-Site value would be cross-site (as https://example.net/ is not same-site with https://example.com/ and https://subdomain.example.com/ ). If that response redirects all the way back to https://example.com/ , the final request’s Sec-Fetch-Site value would still be cross-site (as the redirect chain includes https://example.net/ , which is still not same-site with the other URLs.
4.2. The Sec- Prefix
Each of the headers defined in this document is prefixed with Sec- , which makes them all forbidden header names, and therefore unmodifiable from JavaScript. This will prevent malicious websites from convincing user agents to send forged metadata along with requests, which should give sites a bit more confidence in their ability to respond reasonably to the advertised information.
4.3. Directly User-Initiated Requests
When setting the Sec-Fetch-Site header, user agents are asked to distinguish between navigation requests that are «explicitly caused by a user’s interaction». This somewhat poorly defined phrase is pulled from HTML, which suggests that «A user agent may provide various ways for the user to explicitly cause a browsing context to navigate, in addition to those defined in this specification.»
The goal is to distinguish between «webby» navigations that are controlled by a given (potentially malicious!) website (e.g. links, the window.location setter, form submissions, etc.), and those that are not (e.g. user interaction with a user agent’s address bar, bookmarks, etc). The former will be delivered with a Sec-Fetch-Site header whose value is same-origin , same-site , or cross-site , as appropriate. The latter will be distinguished with a value of none , as no specific site is actually responsible for the request, and it makes sense to allow servers to treat them as trusted, as they somehow represent a user’s direct intent.
Each user agent is likely to have a distinct set of interactions which might fall into one or the other category, and it will be hard to share an automated test suite for these cases. Still, it would be ideal to align on behavior for those which are likely to be common. Sme examples follow:
Navigation from the address bar: In the general case, this kind of navigation should be treated as directly user-initiated, and include Sec-Fetch-Site: none . It may be reasonable for user agents to include heuristics around pasting values into the address bar (especially if the «copy» action can be traced to a specific origin), and to treat such navigations distinctly from those which the user types themselves.
Navigation from user agent UI (bookmarks, new tab page, etc): A user’s interaction with links in user agent UI should be treated similarly to their input in the user agent’s address bar, including Sec-Fetch-Site: none to demarcate the navigation as user-initiated.
Navigation from a link’s context menu (e.g. «Open in new window»): as the link’s target is controlled by the page on which the link is present, user agents should treat the navigation as site-controlled, and set the Sec-Fetch-Site header appropriately for the relationship between the site which controls the link and the site which is being opened.
Ctrl-click on a link: the same arguments and conclusions apply here as apply to a link’s context menu, discussed directly above.
Navigation through history (e.g. a user agent’s «back» button):
Drag-and-drop: It seems reasonable to distinguish behavior here based upon the source of the dragged content. If content is dragged from a tab, the user agent should be able to ascertain its origin, and set Sec-Fetch-Site accordingly. If content is dragged from elsewhere (the user agent’s bookmark bar, another app entirely, etc), then Sec-Fetch-Site: none may be appropriate.
5. Deployment Considerations
5.1. Vary
If a given endpoint’s response depends upon the values the client delivers in a Fetch metadata header, developers should be careful to include an appropriate Vary header [RFC7231], in order to ensure that caches handle the response appropriately. For example, Vary: Accept-Encoding, Sec-Fetch-Site .
5.2. Header Bloat
An earlier version of this document defined a single Sec-Metadata header, whose contents were a dictionary. Subsequent discussion (as well as Mark Nottingham’s excellent [mnot-designing-headers]) shifted the design away from a single dictionary to a series of simple headers, each of which contains only a single token. This design should perform significantly better under HTTP’s current HPACK compression algorithms.
Further discussion on the topic can be found on the review thread in w3ctag/design-reviews#280.
6. IANA Considerations
The permanent message header field registry should be updated with the following registrations for Fetch metadata headers: [RFC3864]