-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTTP responses should be able to indicate that a resource cannot be prefetched/prerendered #138
Comments
@noamr @domenic @yoavweiss @spelchat My impression is that IETF/HTTWG would make a new status code hard (but I don't have deep insight here). Given that I think my preference is for either using a response header field ( |
I agree these are basically the options. I think a pro/con list might help. Here is one for my currently-preferred option, 204:
I'll note that these cons seem small, and can be roughly bounded by a use counter or HTTP archive search that determines how many navigation/subresource responses end up in 204s. Additionally, they can be addressed in the future by introducing a new response header field or a new HTTP status code and urging sites to migrate to those. However, I do think we should try to get IETF first-impressions on the 309 option. If there's a chance that can get some sort of unofficial blessing soon, before we evangelize 204 too hard, that would be even nicer. |
I think so. The other options we/I mentioned earlier are strictly worse I think.
Beyond spec, I assume adoption of a new status code by some origins might be particularly difficult as well. Not sure how much that should inform spec work though.
Me as well. A new header (while slightly awkward), seems more explicit (no confusion with a server that legitimately wants to serve a 204), so I'm leaning very slightly toward that. |
These cons and mitigating factors are non-existent when we supplement 204 with a response header... are there any cons to using a header for the sake of being explicit? |
I checked the HTTP archive for 204. They are very common, but not as navigation. Their main use-cases for
So 204 are used mainly as a server side-effect that doesn't require CORS and doesn't affect UX. |
I feel like 503 is an option that we can explore. The use cases for the opt out I've heard so far are:
|
I like the idea of 503. One con is that if there is a genuine 503, the server will get twice the error rate due to prerender retries. But maybe that's OK as 503 means "retry later" anyway. |
I'm not convinced with the error rate concern. Ideally, I believe server should respond with why they don't want the page to be prerendered, and that seems to be able to be mapped to 4xx/5xx responses (too much load! -> 503) My personal preferences at the moment are:
|
I am less familiar with the implementation specific but I share the same preferences. |
I created a WPT for this that we can populate once we reach consensus. For now it shows that the implementation discards prerendering on 204, 205, and 4xx/5xx. |
After the conversation on Matrix, I am leaning more towards the response header, and to be agnostic to status code.
The main disadvantage is that this flexibility also adds complexity - a response code is very straightforward. I suggest to use the existing |
I'm not sure how to be agnostic to the status code, at least in the spec. Consider a 204 for omnibox prerendering. The desired result when the user types So we need something like:
I guess the spec already has an issue where it's not clear what happens in start a user-initiated prerendering step 6.2 if bc got discarded. So first we'd need to fix that (presumably, with the answer being to do a new navigation from scratch) and then we'd need to say, actually, if we prerendered a 204, do nothing. |
Conceptually I think if we go with a response header then But I don't strongly mind whether |
Okay, so here's an expanded stab at what absorbing this into
Dynamic server behavior in response to |
Yea in the HTML spec a 204 or 205 aborts the navigation but doesn't discard the bc. In this case we have a pre-rendering bc that hasn't navigated. I believe the behavior here should be identical to opening a 204 page directly from the omnibox - as in, do nothing as if enter wasn't pressed. We should verify this in the wpts. |
One disadvantage with allowing this header with any status is that people might start serving it regardless of the request, accidentally disallowing prerenders without realizing. But maybe it's OK and it's a feature rather than a bug. |
I like the SLM proposal that is sketched out in #138 (comment) |
The reason to prerender 4xx/5xx is to avoid fetching them twice. If it took the server a few second and it ended up responding with a 404, we don't want the user to have to send that request again. |
Thank you. I think we should retry 404s too. Let's say that I'd like to book a very popular ticket asap, which is put on sale at 9:00am I'd type a complete URL on the URL bar at 8:55 and I'll hit the enter button as soon as clock hit 9:00am. If prerender triggers at 8:55am and that results in 404 (or other server errors that shows the ticket is not on sale yet) and the navigation commit by the enter button doesn't retry the network fetch, I'd be sad. |
Yea I can see your point. It's a bit of a UX choice, like "the user would refresh if there's an error so let's refresh for them". not sure if there's a correct answer. I feel that some user-agents would want to make that UX choice... which might make things less "correct" in some senses (sending two requests for the same stable 404) but if that's the case it would be better to make it the standard and not bother with a new header. |
Summarizing from some discussions elsewhere: Since UAs are always free to drop prefetches and prerenders (e.g., for resource reasons), even if we don't always drop error statuses the UA can heuristically choose to do so (and re-fetch) when it thinks it's likely to degrade the user experience. It could even do so differently for content-initiated vs chrome-initiated (e.g., address bar) requests. Such choices wouldn't, however, be necessarily interoperable without normative text. So the following behavior could be specified, and could be narrowed in the future if certain cases are widespread:
Options for narrowing this in the future include:
It also leaves to the future the definition of extracting hints, but it seems like it's probably fine to take preload hints for all of these cases if we want to. I'm still not confident we have a principled reason to choose a particular option among the following for the normative text:
The best reason to not rely on the SLM header (but to nominate one or two status codes) that I've seen is that it will require less text to specify and implement for the immediate upcoming objectives Chrome has. Nominating at least one error status for that purpose lets us punt that a little, and happens to be the "middle" (compromise) option. |
On the intent thread, Alex Russell asked:
The response header deals with this straightforwardly |
I found @nyaxt's arguments in #138 (comment) compelling. In particular I think that's the best reason not to rely on the SLM header; it gives a good default behavior for sites that just add some speculationrules or want to be prerendered in the omnibox, while still dynamically switching some URLs between 404s and 200s. If we relied on SLM, such sites would need to use SLM to opt their 404s out of prerendering. More generally, I think a useful framing might be to think about defaults. Both short-term ones that are good for experimentation, and can flexibly be changed pending more feedback, and long-term ones that are best for web developers and the ecosystem. I think "all error statuses abandon" (including 204/205 as error statuses) might be a good default in that regard:
Of course, in addition to the above ecosystem arguments that might cause us to change course, feedback from the community and from other implementers could also do so. In either case, I think "all error statuses abandon" is a reasonable conservative starting point, similar to other cases like delaying workers. |
I can stand behind this ^^ We have to think about iframes as well though. What happens when a browsing context prerenders, but an iframe returns an error status? Do we want to cancel the top level prerender? That would mean that pages that have some small (same-origin) add-on with an error would have to discard prerendering. Perhaps that's for the best in terms of interop, especially at first. |
This is an interesting point. I haven't thought about that. I'm leaning toward allowing them since it happens much later in the stage - we would know the status code of the main page response very early, but it is after that we constructed the DOM of the prerender target page that we know this. Also it can be the case that the subframes are loaded dynamically. |
This means that if a page is put in an iframe it can't opt-out of prerendering. Maybe that's OK. |
Yes. I believe that is OK as long as we are considering same-origin prerendering. |
We always have the option of hanging the load of a subframe if the document isn't okay with prerendering. |
Trying to write out that case seems awkward, because I'm not sure that the counterpart for cross-origin makes sense if we wanted to allow content to expressly permit that in the future. Would it be hard from an implementation perspective to have the same failure condition for subframes, and if they aren't prerenderable we hang the subframe load and retry it on activation? That feels more consistent to me. Is response supported for prerender?
Noting that if the prerendering browsing context fails to create a document (null body, non-HTML MIME type, inappropriate content disposition, etc), this causes an immediate abandonment. Is response supported for prefetch?
In the future, these would be amended to check first for a |
Yes. We can implement behavior (in Chromium) to cancel prerender on certain http response code received for subframe navigations. Note to self: I believe this can be implemented via PrerenderSubframeNavigationThrottle |
@nyaxt SGTM. Filed: https://crbug.com/1299316.
Currently, Chromium cancels prerendering on following HTTP status code for main frame navigations in prerendered pages:
I'm going to include 204/205 here and check it for subframe navigations as well. Is this sufficient? Do I have to handle other status codes? @noamr I'll use your WIP WPTs for verifying the implementation. Thanks! |
One edge-case of the prerendering subframe error case is that when the prerender has a pending subframe navigation (setup for failure) but activation happened before the subframe navigation. |
I think we should allow subframe same-origin error navigation - allowing the main frame to prerender and then starting to reload iframes might disrupt the page. |
I don't have a strong opinion here. I have a mild preference of the current implementation behavior (only discard on the main frame error response), but given that the implementation of all other proposed behavior is straightforward, I'm not against them. @jeremyroman What is your thoughts?
I agree that "reloading iframes on activation" is disruptive. Would it be a problem if we keep the iframes errored (no reload)? |
Yes I'm totally fine with either keeping them errored or discard the whole navigation. I'm leaning towards leaving them errored by default, and letting the UA decide that in certain cases an errored same-origin iframe should discard the whole BC. |
FYI: For now Chromium cancels prerendering on iframe navigation failure [crbug]. We are open to changing the behavior based on the spec resolution. |
At least 204/205 in iframes is a valid use-case ("connected" detection), we shouldn't discard preload when that happens. Added a clarifying PR: #153 |
OK, we will update the Chromium implementation based on this [crbug]. |
This is done and in the spec! |
We need to settle on how servers should indicate in a response that the response is not available right now but that the client should refetch the resource when it actually needs it, because it is likely to succeed then.
I also think there are going to be cases where the main resource isn't prefetchable but related resources (subresources) are, so it would be useful if a response can simultaneously identify such subresources. This is a little messy to do with 400-599 responses without some work, because it isn't clear whether those links apply to the requested resource or to the returned error.
In any case, servers should be careful about the cache lifetime of such responses. If they're different depending on
Sec-Purpose
, they might need to send aVary
response header to indicate this, if it would otherwise be cacheable.New HTTP status code
Define a new HTTP status code, along these lines:
And then specify that prefetch/prerender send a
Sec-Purpose
request header field and interprets a Not Ready response as indicating the prefetch cannot proceed but we might fetch subresources. This requires a minor edit to Fetch to specify how this status code is treated there.This is very explicit and semantically correct, but the IETF/HTTPWG process may be long and involved. Still, we could ask HTTPWG what they think.
Sample exchange:
New HTTP response header field
Define a new response header field, along these lines:
Advise clients to serve such a response when a response body suitable for prefetch with the parameters indicated in the
Sec-Purpose
request header field is not available. Since the body will be discarded, this should be a204 No Content
response, but servers may serve another status.The generic name anticipates the possibility of using this as an extension point for varying the maximum prefetch lifetime or similar things in the future, if required, and is similar to
Cache-Control
.Sample exchange:
This is slightly awkward semantically, but not dramatically so. Web browsers have felt more free to introduce new header fields than status codes, so this path is more well-worn. This also opens the possibility of doing scanning for an equivalent
<meta>
tag in the response body in the future.Using 204 No Content
Define a 204 No Content in response to
Sec-Purpose: prefetch
requests to indicate that the response body is unavailable, but that response header fields which describe the resource might still be present. This is analogous to a 204 response to a PUT request, or to a 304 response to a conditional GET (but note that a separate status code was used for the latter).Sample exchange:
Error status codes
Define that all errors
400-599
force prefetching and prerendering to fail.This has the advantage that we get a retry of content which has failed to load but which might have a non-error response when the user actually navigates (e.g., a
503 Service Unavailable
response but the server became available minutes later). However, it slightly muddles the meaning of these responses and makes prefetching/prerendering resources which do have such statuses impossible. It may also lead to authors seeing elevated error rates.Sample exchange:
Still respecting
Link
hints here, if we wanted to do so, would be slightly awkward because it's not obvious whether they describe the error resource or the indicated resource. We could consider extendingLink
to disambiguate, or if we restrict to 204 responses only, specifying that this is the meaning of Link header fields in 204 responses to Sec-Purpose: prefetch requests.Respond to cache behavior
Declare that only resources which are cacheable to some extent are eligible for prefetch and prerender, and authors should reject prefetch/prerender by returning a non-cacheable response.
This means that some resources which are extremely variable will avoid prefetching/prerendering due to their existing headers. However it is even more awkward a fit for prerendering than it is for prefetching (because prerendering resembles an HTTP cache much less), and would make such responses rejecting prefetch harder to cache (though
CDN-Cache-Control
would still make it possible).Still respecting
Link
hints here, if we wanted to do so, would be slightly awkward because it's not obvious whether they describe the error resource or the indicated resource. We could consider extendingLink
to disambiguate.Sample exchange:
The text was updated successfully, but these errors were encountered: