Sec-Fetch-Site
request headersecurity
Sent by browsers to say whether a request came from your own origin, your site, another site, or the user.
Current browsers attach Fetch Metadata headers to every request: Sec-Fetch-Site (who initiated it), Sec-Fetch-Mode (navigate, cors, no-cors…), Sec-Fetch-Dest (document, image, script…) and Sec-Fetch-User. Pages can't forge them, so a server can reject cross-site requests to sensitive endpoints before doing any work — a strong defence against CSRF and cross-site leaks.
Syntax
Sec-Fetch-Site: same-origin | same-site | cross-site | none
Defined in W3C — Fetch Metadata Request Headers.
Values
| same-origin | from a page on the same origin |
|---|---|
| same-site | from another origin on the same site, such as a sibling subdomain |
| cross-site | from a different site |
| none | user-initiated: a typed URL, bookmark or new tab |
Examples
Sec-Fetch-Site: cross-site
a request triggered by another website
Sec-Fetch-Site: none
the user typed the address
Good to know
- Allow
same-origin,noneand (if you trust your subdomains)same-site; rejectcross-siteexcept for top-level GET navigations, so links into your site keep working. - Older browsers and non-browser clients don't send these headers — let requests without them through, and keep CSRF tokens or SameSite cookies for those.
- The
Sec-prefix marks a forbidden header: page scripts can't set or change it.
Further reading: SameSite cookies explained
Using Sec-Fetch-Site on the server
Browsers set Fetch Metadata headers themselves. Your part is the server-side policy that reads them:
app.use((req, res, next) => {
const site = req.get("Sec-Fetch-Site");
if (!site) return next(); // older browser or non-browser client
if (["same-origin", "same-site", "none"].includes(site)) return next();
// Let cross-site links into the site work
if (req.get("Sec-Fetch-Mode") === "navigate" && req.method === "GET") return next();
res.status(403).send("Cross-site request blocked");
});Check it
In the browser, open DevTools → Network, pick a request and look under Request Headers. From the command line, curl's verbose mode prints every header it sends, prefixed with >:
curl -sv https://example.com/ -o /dev/null 2>&1 | grep '^>'
Frequently asked questions
Do I still need CSRF tokens?
All current major browsers send Fetch Metadata, but older browsers and other clients don't. Use it as a strong first check, and keep tokens or SameSite cookies as well.