What does the Origin request header tell a server about an incoming POST, and what does it not?
answer
- who writes the field, not who reads it
- names the initiator, not the target
- user agent appends it, script cannot
- mismatch is evidence, absence is not
- scheme, host and port only
basics
~20 sA conforming user agent, not page script, writes the Origin request header, so a value that is not yours means another document initiated the request. Its absence proves nothing, and a client that is not a browser can send any value.
solid answer
~50 s`Origin` names the origin of the document or script that **initiated** the request, never the one being addressed. A conforming user agent computes it and attaches it whenever the method is neither GET nor HEAD, and for any request in cross-origin-sharing mode; a page's script may not set or override it, and a cross-site document that auto-submits a form has no way to influence it. So on a cookie-authenticated booking endpoint, an `Origin` that is not on your allowlist is strong evidence the request was initiated by a document you did not serve. What it does not give you: absence is not proof of a same-origin initiator, the field can arrive carrying the literal `null`, and a non-browser client puts whatever it likes there. It is a second signal beside a token, not the defence.
code
http · 9 linesPOST /api/bookings HTTP/1.1
Host: booking.example.com
Origin: https://promo.evil.test
Referer: https://promo.evil.test/win-a-class
Content-Type: application/x-www-form-urlencoded
Cookie: session=8f2c1b...
Content-Length: 27
classId=91&action=cancelgo deeper
Recall that the browser writes the Origin request header and the page cannot, that it names the document that started the request, and that it holds scheme, host and port with no path.
Explain when a user agent appends the field, why a cross-site form post therefore carries it, and why absence carries no information at all rather than implying a same-origin request.
Show the failure you have actually seen: a check that passes because the header was missing, or one relied on as the sole defence on an endpoint reachable by a state-changing GET.
Frame it as a corroborating signal with known blind spots, and argue for where the residual risk is carried — by an unguessable per-session value — rather than by hardening a provenance hint.
## The field, and who writes it The `Origin` request header field names the **origin of the document or script that initiated the request** — not the origin being addressed. Its value is serialized as `scheme "://" host [ ":" port ]`: no path, no query string, no trailing slash, and the scheme's default port omitted. RFC 6454 calls the field's grammar `origin-list-or-null`; in practice a conforming user agent sends one serialized origin or the four-character literal `null`. The property that makes the field usable as a forgery signal is **who writes it**. The user agent derives the value from the initiating document and appends it itself. A page's own script may not set or override it on a request it makes, and a cross-site document that auto-submits a form has no opportunity to influence it at all. That is the whole difference between `Origin` and, say, a custom header your own client adds: an attacker's page cannot dress its request up as one of yours. ## When a server can expect the field to arrive - A conforming user agent appends `Origin` whenever the request's **method is neither GET nor HEAD**. A forged `application/x-www-form-urlencoded` form post from a hostile document therefore arrives carrying the field. - It also appends it for any request made in **cross-origin-sharing mode**, whatever the method. - It does **not** append one to an ordinary top-level GET navigation, so a request that changes state on GET is invisible to this check. - Presence is not the same as usefulness. The field can arrive carrying the literal `null`, which identifies nothing and must never sit on an allowlist. ## What each value actually supports | What arrives | What a server may conclude | |---|---| | your own serialized origin | a document of your origin initiated it, as computed by a conforming user agent | | some other serialized origin | a document you did not serve initiated it — refuse | | the literal `null` | the initiator could not be usefully serialized — unverifiable, refuse | | no field at all | nothing; several unrelated causes produce this | Read the table in one direction only. A matching value rules out a *cross-site document* as the initiator. It does not rule out a script running on your own pages, and it does not rule out a caller that is not a browser at all. ## What the field does not prove - **Absence is not same-origin.** A missing `Origin` has several causes, and none of them is evidence that the request came from your own pages. Treating absence as a pass is the classic way a check like this is defeated. - **It is not an authentication of the user.** The session credential does that. `Origin` speaks only to provenance: which document set the request in motion. - **It says nothing about a non-browser client.** Anything that speaks the protocol directly composes whatever headers it likes. A check on `Origin` is not access control for API clients; it is a statement about what a browser would have done. - **It cannot see intent.** A script injected into your own page produces a perfectly valid `Origin`. Provenance and authorisation are different questions. - **A present field can still be worthless.** `null` is present and carries no identity. ## Where the check belongs in a defence For a scripted booking client that calls its own API with the session cookie attached, there is no server-rendered form in which to hide a token, so the temptation is to lean on `Origin` alone. Resist it. The check is cheap, stateless and catches the ordinary cross-site forgery immediately, but it has three soft edges — the state-changing GET with no field, the `null` value, and the request with no field at all — and every one of them has to resolve to a refusal rather than a fall-through. The primary defence stays a value the cross-site document cannot read and therefore cannot replay; the initiator check sits beside it as corroboration. The practical rule to carry away: **a value you do not recognise is a reason to refuse; a value you do recognise is a reason to keep going, not a reason to stop checking.**
- Does a cross-site form post really carry an Origin request header, or only a scripted call?It really carries one. A conforming user agent appends `Origin` on any request whose method is neither GET nor HEAD, independent of who emitted it or which form encoding was used, so a hostile auto-submitting form posting `application/x-www-form-urlencoded` arrives with the field naming the attacker's document.
- If the Origin value matches your own site, has the request been proven legitimate?No. It means a conforming user agent computed the initiator's origin as yours, which rules out a cross-site document. It does not rule out a caller that is not a browser, and it does not rule out script injected into your own pages, which produces a valid value. The token remains the defence.
- Why does the field help at all if a non-browser client can put anything in it?Because the attack it addresses is specifically a browser being driven by a document you did not serve, with the session credential attached by the user agent. In that scenario the attacker controls the page but not the field. A non-browser caller has no ambient session cookie to abuse in the first place.
It is the postmark a sorting office stamps on an envelope, not the return address the sender writes. A forger controls the letter but not the postmark — and an envelope that arrives with no postmark at all tells you nothing about where it was posted.
saying these in an interview costs you the question
- Thinks an attacker's page can set the Origin header on its own form post.
- Treats a missing Origin header as proof the request was same-origin.
- Believes the Origin value carries the full request URL including its path.
- Says Origin names the site being addressed rather than the initiating document.
- Assumes every request the server receives was produced by a browser.
- Calls an Origin check a complete cross-site request forgery defence on its own.