# Sec-Fetch-Site Header — Syntax, Values & Examples

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

- Type: Request header
- Category: Security
- Defined in: [W3C — Fetch Metadata Request Headers](https://www.w3.org/TR/fetch-metadata/)

## Syntax

```http
Sec-Fetch-Site: same-origin | same-site | cross-site | none
```

## Values

| Value | Meaning |
|---|---|
| `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

```http
Sec-Fetch-Site: cross-site
```

a request triggered by another website

```http
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.

## 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 `>`:

```sh
curl -sv https://example.com/ -o /dev/null 2>&1 | grep '^>'
```

## FAQ

### 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

- [403 Forbidden](https://www.devkult.com/http/403.md)

## Related headers

- [Origin](https://www.devkult.com/headers/origin.md): Sent by browsers to say which origin — scheme, host and port — a request comes from.
- [Referer](https://www.devkult.com/headers/referer.md): The address of the page that linked to or loaded the requested resource.
- [Set-Cookie](https://www.devkult.com/headers/set-cookie.md): Stores a cookie in the browser, with attributes that control its lifetime, scope and security.

## Further reading

- [SameSite cookies explained](https://www.devkult.com/blog/samesite-cookies-explained.md)

Open the interactive page: https://www.devkult.com/headers/sec-fetch-site