Frame-redirect following
When a request made by a Turbo Frame gets a redirect, Turbo follows it and loads the target inside the frame. After
a form submit, you usually want the whole page to go there instead. TurboFrameListener makes that the default,
without any change to your controllers:
- Turbo FramePOST, Turbo-Frame: project-edit
- Controller302, Location: /projects/1
- TurboFrameListener204, Turbo-Location: /projects/1
- Browserfull Drive visit of /projects/1
The browser side is the turbo:before-fetch-response listener of TurboHandler:
it reads Turbo-Location, cancels the frame render and visits the URL.
When a redirect is converted
Section titled “When a redirect is converted”The listener subscribes to kernel.response. It replaces the response with an empty 204 No Content carrying a
Turbo-Location header set to the original Location when:
- the response is a redirection (
3xx) with aLocationheader: a304 Not Modified(a frame revalidated with anETag) is left alone, - and the request has no
Turbo-Frame-Follow-Redirectheader (the frame keeps its redirects), - and one of:
- the request has a
Turbo-Frameheader, - the request is a
DELETEwith anX-Turbo-Request-Idheader (sent by Turbo 8 with its requests, not by Turbo 7), andfollow_delete_redirectsis on.
- the request has a
The 204 keeps the headers of the redirect response, cookies and Cache-Control included: only the status, the
Location header (moved to Turbo-Location) and the body headers (Content-Type, Content-Length) change. Flash
messages live in the session, whose cookie Symfony adds later in kernel.response.
Any other response passes through unchanged.
DELETE redirects
Section titled “DELETE redirects”A 302 answered to a DELETE fetch is followed by the browser with the same DELETE method, on the redirect target.
Converting the redirect into a Turbo-Location visit avoids that, for frame and non-frame Turbo requests alike, except
for frames that keep their redirects.
Turn it off with the follow_delete_redirects option, or the followDeleteRedirects
constructor argument without the framework:
silarhi_turbo: follow_delete_redirects: falseKeeping a redirect in its frame
Section titled “Keeping a redirect in its frame”Add data-turbo-follow-redirect to a <turbo-frame> to opt it out of the escalation: the handler sends a
Turbo-Frame-Follow-Redirect header with that frame’s requests, and TurboFrameListener leaves all their redirects
untouched, DELETE ones included. See Keeping a redirect in its frame.
Without @silarhi/turbo
Section titled “Without @silarhi/turbo”Something in the browser must follow the Turbo-Location header: on its own, Turbo receives an empty 204 and the
frame does not change. If you don’t use TurboHandler, add the equivalent listener:
import * as Turbo from '@hotwired/turbo'
document.addEventListener('turbo:before-fetch-response', (event) => { const location = event.detail.fetchResponse.response.headers.get('Turbo-Location') if (location) { event.preventDefault() Turbo.visit(location) }})The HTTP headers reference lists the whole contract between the two halves.
