Repository navigation
An origin's redirect to itself keeps the reader on the address they asked for #1330
Description
Stacks on the publisher origin in a folder of object storage (#1329).
What changes
When the origin answers a request with a redirect whose Location names the origin, the reader is sent to the same path, query and fragment on the scheme and host they asked for, so their browser stays on the deployment. There is no setting.
With origin_url = "https://origin.example" and a reader who asked for https://publisher.example/news:
| The origin answers | The reader receives |
|---|---|
301 with Location: https://origin.example/news/ |
Location: https://publisher.example/news/ |
302 with Location: https://origin.example/news/?page=2#latest |
Location: https://publisher.example/news/?page=2#latest |
301 with Location: https://www.origin.example/news/ |
The header as the origin wrote it |
301 with Location: /news/ |
The header as the origin wrote it |
200 or 201 with Location: https://origin.example/news/ |
The header as the origin wrote it |
How it works
- Which responses. A status from
300to399with aLocationheader. ALocationon any other status is left as written. - What names the origin. The host of the
Location, with its port, compared with theHostthe origin was sent, which isorigin_host_header_overridewhere one is set and otherwise the host oforigin_urlwith its port, ignoring letter case. The scheme is not compared, and aLocationthat leaves the scheme off is read on the scheme of the request. - The origin path. Where
origin_urlhas a path, aLocationat or beneath that path comes back without it, and one outside it is left as written, because no address on the reader's host leads there. - What is left alone. A
Locationnaming any other host, a host beneath the origin's, one that names no host, which the reader's browser resolves against the address it asked for, a value that cannot be read as an address, and anyLocationwhen the request names no host. - Where it happens. In one place, where
handle_publisher_requestreceives the origin's response, so the streamed page, the buffered page, aHEADand every other method carry the rewritten header.
Why
An origin redirects to itself for ordinary reasons, a request path without its trailing slash being enough, and it names itself in the Location because that is the name it was asked for. Relayed as written, the header takes the reader's browser to the origin, and from then on each page is the origin's own answer with none of the publisher's middleware run on it, at an address the publisher never gave the reader. No deployment asks for that by naming an origin, so there is no setting. The rule uses only the origin's own name and path and the request's own scheme and host, names no vendor and reads no module's settings, so it is core's.
What breaks
Nothing for a deployment whose origin sends no redirect to itself. One whose origin does now keeps its readers, where before they left for the origin.
Draft on the fork, jwrosewell#32, on top of the publisher origin in a folder of object storage (fork pull request 31). The design page is docs/superpowers/specs/2026-10-13-origin-redirect-design.md on that branch.
How it was verified
CI on the fork. The tests in core cover a Location naming the origin coming back on the request's scheme and host with its path, query and fragment, for an origin on https and on plain HTTP, with and without a port, in any letter case and with the scheme left off, the override compared where one is set, another host, a host beneath the origin's and the origin's name on a different port left as written, the origin path removed where the Location is at or beneath it and left where it is not, and through handle_publisher_request with a scripted origin, the rewritten header on a buffered answer and on a streamed page for a GET, a HEAD, a POST, a PUT and a DELETE across 301, 302, 303, 307 and 308, with a redirect to another host, one naming no host, a Location on a 200, a 201, a 404 and a 503, and a redirect with no Location each reaching the reader as the origin sent them.
Activity