When does the browser send the Origin header? (tested)
The Origin header tells a server which site a request came from. It is the basis of CORS and a common CSRF check, but browsers do not send it on every request. We logged what Chromium actually sent.
1. What the server received
Page at http://localhost:8971. "Cross-origin" means a request to http://127.0.0.1:8972.
| Request | Origin sent | Referer sent |
|---|---|---|
fetch() GET, same-origin | (absent) | full page URL |
fetch() POST, same-origin | http://localhost:8971 | full page URL |
fetch() PUT, same-origin | http://localhost:8971 | full page URL |
fetch() GET, cross-origin | http://localhost:8971 | origin only |
fetch() POST, cross-origin | http://localhost:8971 | origin only |
<form method=POST>, same-origin | http://localhost:8971 | full page URL |
<form method=POST>, cross-origin | http://localhost:8971 | origin only |
<form method=GET>, same- or cross-origin | (absent) | sent |
<img>, cross-origin | (absent) | origin only |
<img crossorigin>, cross-origin | http://localhost:8971 | origin only |
<img crossorigin>, same-origin | http://localhost:8971 | full page URL |
| Link click (top-level navigation), cross-origin | (absent) | origin only |
The pattern: Chromium sent Origin on every request in CORS mode (cross-origin fetch, crossorigin images) and on every non-GET request, same-origin included. It left Origin off same-origin GET fetches, GET form submissions, plain images and link clicks. So a missing Origin on a GET is normal and tells you little.
2. When Origin is the string "null"
| Request | Origin sent | Referer sent |
|---|---|---|
Page with Referrer-Policy: no-referrer, fetch() GET or POST (same- or cross-origin) | http://localhost:8971 | (absent) |
Page with Referrer-Policy: no-referrer, cross-origin <form method=POST> | null | (absent) |
Sandboxed iframe (sandbox="allow-scripts"), fetch() GET or POST | null | (absent) |
Referrer-Policy: no-referrer removed Referer everywhere, but it did not hide the origin from fetch(). It did turn the Origin of a cross-origin form POST into the literal string null. A sandboxed iframe has an opaque origin, so everything it sent carried Origin: null. Data: URLs and redirects across origins are documented to produce null too; we did not test those. Never put null in an Access-Control-Allow-Origin allowlist: any sandboxed iframe on any site can send it.
3. Origin is not authentication
Any non-browser client can send whatever Origin it likes. Against a small test server:
$ curl -s -H 'Origin: https://bank.example' http://127.0.0.1:8973/
Origin seen: https://bank.example
Browsers do not let page scripts set or change Origin, which is why checking it works as a CSRF defense: a malicious page can make the browser send a POST with your user's cookies, but it cannot make the Origin say your site. That protection only applies to requests from browsers. It does nothing against a script, a bot or curl, so you still need real authentication on the API.
4. Using Origin for a CSRF check
- For state-changing requests (POST, PUT, PATCH, DELETE), compare Origin with an exact list of your own origins (scheme, host and port).
- Decide what to do when Origin is missing or
null. In our test a no-referrer page's cross-origin form POST and sandboxed iframes sentnull; rejectingnullon state-changing requests is the safe default. - Keep state changes off GET. Chromium sent no Origin on GET forms or link clicks, so an Origin check cannot protect them.
- Combine it with SameSite cookies or a CSRF token if you need defense in depth (doc-based advice, e.g. the OWASP CSRF cheat sheet).
Related: allowing multiple origins in CORS, Access-Control-Allow-Credentials and the Referrer-Policy header.
Frequently asked questions
Does the browser send the Origin header on GET requests?
Only sometimes. In our Chromium 153 test, cross-origin fetch GET and img crossorigin sent it; same-origin fetch GET, GET form submissions, plain img tags and link clicks did not.
Does the browser send Origin on same-origin requests?
Yes for POST and PUT: Chromium sent Origin on same-origin fetch POST/PUT and same-origin form POST. It did not on same-origin fetch GET.
Why is my Origin header null?
In our test Origin was the string null for requests from a sandboxed iframe and for a cross-origin form POST from a page with Referrer-Policy: no-referrer. Data: URLs and some cross-origin redirects are documented to do the same (not tested).
Does Referrer-Policy: no-referrer remove the Origin header?
No for fetch: Chromium still sent the real origin on fetch GET and POST, while Referer disappeared. A cross-origin form POST from that page sent Origin: null.
Can the Origin header be spoofed?
Not by page JavaScript in a browser, which is why it works for CSRF checks. Any non-browser client can: curl -H 'Origin: https://bank.example' delivered exactly that value to our server. Do not use it as authentication.
Should I allow Origin null in CORS?
No. Any sandboxed iframe on any site sends Origin: null, so allowing it lets arbitrary pages read your responses.
Written with AI assistance. Measured on 2026-10-01 in full Chromium 153.0.8010.12 (Playwright, channel 'chromium', fresh browser context per row). The page was served from http://localhost, a second Node.js server on http://127.0.0.1 (a different origin) logged the Origin and Referer headers of every request, and the same-origin server logged them too. The curl check used curl 8.14.1. Notes on Firefox and Safari and on server frameworks are doc-based, not tested. PlainHeaders is our extension; the other methods are free.