Origins and Cross-Site Requests
Sending Is Not Reading
A page can show an image that lives on any other site. It can link to any other site, and it can hold a form that posts to any other site. The web was built that way. Each of those is a request that one site’s page starts and another site’s server answers.
Your model can already say which
Three questions
For any request a page starts, a browser answers three questions, and it answers them separately.
a page a server
|
| 1. Is the request sent? Almost always.
| 2. Does the cookie go along? Host, path, Secure, SameSite.
|
| ------------------ the request -------------------> |
| | the server
| | acts
| <----------------- the response ------------------- |
|
| 3. May the page read the response?
| Only when it comes from the page's own origin.
The first question is whether the request is sent at all. The answer is almost always yes. The
The second is whether a cookie goes along. That is Lesson 3: the host, the path, Secure, and SameSite, which compares sites.
The third comes after the server has acted and answered: may the page that started the request read the response? This one compares
The policy is about reading
The rule behind the third question is the
Read that sentence again and look for the word “send”. It is not there. The policy says what a page may read. Nothing in it stops the request from leaving, and nothing in it mentions cookies. Chapter 9 shows how a server can give another origin permission to read.
The full policy covers more than responses. It also has rules for frames, for data a page stores in the browser, and for scripts loaded from another origin. This lesson stays with responses.
The case table
Here are six requests to https://reading.example. In each case the jar holds Ana’s session cookie, which is SameSite=Lax.
| The page is on | It starts | Sent? | Cookie? | May the page read the response? |
|---|---|---|---|---|
other.example | an image | yes | no | no |
other.example | a form post | yes | no | no |
other.example | a followed link | yes | yes | no |
notes.reading.example | an image | yes | yes | no |
notes.reading.example | a form post | yes | yes | no |
reading.example | a script’s fetch | yes | yes | yes |
In the third row the reader ends up on the reading
The next block prints the same table. Every request is sent, so it has no column for that. The cookie column is computed by a short copy of the Lax rule you wrote, and the last column by comparing origins. Run it. Then change a row, for example the "GET", and run it again.
A page on https://other.example contains an image whose address is https://reading.example/me. What happens?
The same image sits on a page of https://notes.reading.example. What changes?
What follows
Look at the fifth row once more. A page on notes.reading.example posts a form to the reading list. The request is sent. Ana’s cookie goes along, because the neighbor is on the same site. The server sees a POST with a valid
That page does not need the response. If the form said “delete book 1”, the book was gone before any response existed.
A page cannot read the response to a form it posted to another origin. Why can the post still do harm?
So the same-origin policy protects what your server says, and it does not protect what your server does. A request that rides on someone’s cookie to make a change they never asked for is called