Request header
> Sec-Fetch-Dest: documentSec-Fetch-Dest Header: All Values Explained
Sec-Fetch-Dest tells the server where a response will be used: document, iframe, image, script, empty. Full value list and server-side uses.
- Direction
- Request
- Category
- Security
- JS can set it
- No: forbidden header name
TL;DR:
Sec-Fetch-Desttells your server what the browser will do with the response: render it as adocument, put it in aniframe, use it as animage, run it as ascript, or hand it to JavaScript (empty). It lets you refuse, for example, to serve a JSON endpoint to an<img>tag or a private page to an<iframe>on another site.
Example
GET /avatar.png HTTP/1.1
Host: example.com
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross-site
This is a cross-site <img>: another site is hotlinking your image. The header is a structured-field token (section 2.1 of the W3C Fetch Metadata spec) and a forbidden request header, so scripts cannot set it.
Values
From MDN’s current list, with what produces each:
| Value | Source |
|---|---|
audio | <audio> |
audioworklet | audioWorklet.addModule() |
document | A top-level navigation |
embed | <embed> |
empty | fetch(), XMLHttpRequest, navigator.sendBeacon(), EventSource, WebSocket and other requests with no specific destination |
fencedframe | The Fenced Frame API (Chromium only) |
font | CSS @font-face |
frame | <frame> |
iframe | <iframe> |
image | <img>, SVG <image>, CSS background-image and similar |
json | import ... with { type: "json" } |
manifest | <link rel="manifest"> |
object | <object> |
paintworklet | CSS.paintWorklet.addModule() |
report | CSP and other violation reports |
script | <script>, importScripts() |
serviceworker | navigator.serviceWorker.register() |
sharedworker | new SharedWorker() |
style | <link rel="stylesheet">, CSS @import, import ... with { type: "css" } |
text | import ... with { type: "text" } |
track | <track> |
video | <video> |
webidentity | FedCM identity endpoints |
worker | new Worker() |
xslt | XSLT transforms |
The list follows the Fetch Standard’s request destinations and grows with the platform, so write server rules as an allow-list for the destinations you expect rather than a deny-list of known-bad ones.
Practical uses
- Refuse to be framed or embedded. A cross-site request with
Sec-Fetch-Dest: iframe,frame,embedorobjectto a page that has no business being framed can get a403.frame-ancestorsin Content-Security-Policy is the browser-enforced way to do it, and it covers browsers that do not send this header. - Stop JSON endpoints being loaded as scripts. An API that only serves
emptydestinations should rejectscript, which is the shape of old JSONP-style cross-site data theft. - Cut hotlinking. Reject
image,videooraudiodestinations whenSec-Fetch-Siteiscross-siteand send an alternative. - Separate navigations from XHR.
documentis a person opening a page,emptyis code calling an API. Same URL, different handling, withVary: Sec-Fetch-Destif anything is cached.
# Serve downloads only to navigations and same-site code, never to cross-site embeds
map "$http_sec_fetch_site:$http_sec_fetch_dest" $block_embed {
default 0;
"~^cross-site:(iframe|frame|embed|object)$" 1;
}
Then if ($block_embed) { return 403; } in the relevant location. The full policy that combines Dest with Site and Mode is on the Sec-Fetch-Site page.
Browser support
Chrome and Edge 80, Firefox 90, Safari 16.4. Sent only to potentially trustworthy URLs (HTTPS and localhost). Absent for curl and server-side clients.
Related
Frequently asked questions
What does Sec-Fetch-Dest: empty mean?
empty is the token for requests with no specific destination, which is what fetch(), XMLHttpRequest, navigator.sendBeacon(), EventSource and WebSocket produce. The Fetch Standard represents this destination as the empty string, and the Fetch Metadata spec maps it to the token empty because a structured-field token cannot be blank.
How do I stop my files being embedded in iframes using Sec-Fetch-Dest?
Reject requests with Sec-Fetch-Dest of iframe, frame, embed or object when they come from another site. For a response-side control that browsers enforce for you, use Content-Security-Policy frame-ancestors; the Sec-Fetch-Dest check is useful when you want to refuse to serve the bytes at all.
Is Sec-Fetch-Dest sent in Safari?
Yes, since Safari 16.4. Chrome and Edge have sent it since 80 and Firefox since 90. The fencedframe value is only available in Chromium, and non-browser clients send none of the Sec-Fetch headers.
Can I serve different content based on Sec-Fetch-Dest?
You can, for example returning an app shell for document and JSON for empty at the same URL, but you must add Vary: Sec-Fetch-Dest to the response or a shared cache will hand the wrong representation to the next client. Separate URLs are simpler to reason about.
Sources
Related
Sec-Fetch-Mode Header: Values and Server Use
Sec-Fetch-Mode reports the request mode: navigate, cors, no-cors, same-origin or websocket. What sets each value and how servers use it.
Sec-Fetch-Site Header: Block Cross-Site Requests
Sec-Fetch-Site says whether a request is same-origin, same-site, cross-site or user-initiated. Resource isolation policy for Express and nginx, plus CSRF use.
Sec-Fetch-User Header: ?1 and User Activation
Sec-Fetch-User is always ?1 and appears only on user-activated navigations, like a link click. When it is absent and why Safari does not send it.
Content-Security-Policy Header: Directives, Nonces and Console Errors
Content-Security-Policy (CSP) limits what a page may load or run. Directives, nonces, strict-dynamic, Report-Only rollout, console errors, helmet and Next.js.