devkult_
tools29converters34
home/headers/sec-fetch-site

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-originfrom a page on the same origin
same-sitefrom another origin on the same site, such as a sibling subdomain
cross-sitefrom a different site
noneuser-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, none and (if you trust your subdomains) same-site; reject cross-site except 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:

Express middleware
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.

Status codes it appears with
Related headers