Safer Request Headers: Fetch Metadata Request Headers
One day while developing a page, I opened DevTools to check whether the request header fields were correct and noticed a few suspicious extra headers in the request.
Notice anything unusual? Taking a closer look, why are there three headers starting with Sec-Fetch-*?
This caught my attention. I hadn’t set these headers, nor had I used any libraries that would. Why were they being added automatically? My guess was that the browser was doing something behind the scenes.
After searching on Google, I discovered that this is a new draft specification called Fetch Metadata Request Headers. Currently, only Chrome adds these headers; Firefox and Safari do not.
Revisiting Same-Origin Policy (CORS)
As we all know, to prevent malicious actors from using tricky methods to steal your data, browsers restrict access conditions from certain origins—for instance, requiring Access-Control-Allow-Origin headers or preflight requests for cross-origin requests. For more details, you can refer to my previous post, “Dealing with CORS and Cookies”.
Even so, there are still flaws. For example, <img/> tags can still send cross-origin requests, allowing malicious users to bypass CORS restrictions. Of course, if the server implements basic defenses, it can still block most insecure requests.
If extra header information (metadata) could be attached when sending a request to describe where it originated, the server could respond accordingly based on these headers. That is why this draft was introduced.
Header Specifications:
Currently, there are four main headers in the draft:
1. Sec-Fetch-Dest
Indicates the destination of the request.
Possible values include audio, document, font, image, object, serviceworker, and more. This provides several benefits. With this header, the server can immediately tell whether the context of the request is legitimate. For example, if a request comes from an <img> tag but isn’t asking for an image, it is very likely an attacker, and we can immediately return an error response.
2. Sec-Fetch-Mode
Indicates the mode of the request. Main values include cors, navigate, nested-navigate, no-cors, etc., which identify the request’s mode, similar to the mode option in fetch.
With headers like Set-Fetch-User, we can also know whether the user initiated the request through an interaction (such as a click, keystroke, etc.).
3. Sec-Fetch-Site
Indicates whether the origin of the request is same-origin or cross-origin.
4. Sec-Fetch-User
This header carries a boolean value. It is only true if the request is a navigation request (where the request destination is document) and was triggered by user interaction (such as clicking a button, pressing a key, etc.). The server can use this header to determine whether the user’s method of triggering the request is legitimate.
For example, when submitting a form via a click, because the request is sent by a document and involves user interaction (clicking the button to submit), Set-Fetch-User will be ?T.
Is There a Way to Remove These Headers?
If this becomes part of the formal specification, there likely won’t be a way to remove them. In addition, you cannot manually overwrite their values because they are classified as forbidden headers.
Conclusion
In the past, achieving something similar usually required custom headers, but those could still be spoofed. Furthermore, adding custom headers triggered preflight checks, creating extra overhead. The benefit of tackling this at the specification level is that these headers cannot be easily modified, and they provide a unified standard. With these headers in place, backends can perform additional checks upon receiving requests, thereby enhancing security.
Related Posts
- Recreating My Room with Three.js Using React Three Fiber, I brought my real room into the browser—turning physical objects into an interactive table of contents, and using spatial memory to tell the story of my life and work over the past few years.
- Things to Keep in Mind When Using Images in Frontend Development Expanding on Jake Archibald's article, this post organizes how modern responsive images should be written: why width/height are still necessary, when to use CSS aspect-ratio, how to choose between AVIF and WebP, and using picture/source/srcset for art direction on mobile devices.
- CSS field-sizing — Auto-resize Form Elements with a Single Line of CSS Previously, auto-resizing a textarea required listening to scrollHeight in JavaScript. With CSS field-sizing: content, a single line replaces it all, supporting textarea, input, and select. This article covers the pain points of older approaches and how to use field-sizing.
- Make Your Link Underlines Look Better: text-underline-offset By default, underlines sit very close to the text. Some designers dislike this look, and personally, I don't think it looks great either.