Prefetch

Draft Community Group Report,

This version:
https://wicg.github.io/nav-speculation/prefetch.html
Issue Tracking:
GitHub
Inline In Spec
Editor:
(Google)

Abstract

Extensions to WHATWG Fetch for prefetching with partitioning in mind.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Concepts

In light of storage partitioning, this specification defines prefetch for navigations which would occur within the same partition (for example, top-level navigations within the same site) and for navigations which would occur in a separate partition (for example, top-level navigations to a different site).

Conflicting credentials exist for response response given navigable navigable and network partition key sourcePartitionKey if the following steps return true:
  1. Let hypotheticalEnvironment be the result of creating a reserved client given navigable, response’s URL, and null.

  2. Let hypotheticalPartitionKey be the result of determining the network partition key given hypotheticalEnvironment.

  3. If hypotheticalPartitionKey is equal to sourcePartitionKey or there are no credentials associated with URL and hypotheticalPartitionKey, then return false.

  4. Let loadingModes be the result of getting the supported loading modes for response.

  5. If loadingModes contains `uncredentialed-prefetch` then return true.

  6. Return false.


An exchange record is a struct with the following items:

These records can be used to defer checks that would ordinarily happen during a navigate fetch, and to check for modified credentials.

A redirect chain is a list of exchange records.

To update the response for a redirect chain redirectChain given a request request and response response:
  1. Assert: redirectChain is not empty.

  2. Assert: redirectChain’s last element’s request is the same as request and its response is null.

  3. Set redirectChain’s last element’s response to response.


Each Document has prefetch records, which is a list of prefetch records.

A prefetch record is a struct with the following items:

This tracks prefetches from when they are started to when they are ultimately used or discarded. Consequently some of these fields are immutable, some pertain to the ongoing activity (like fetch controller), and some (like expiry time) are populated when the prefetch completes.

Unless the response indicates otherwise using `Supports-Loading-Mode`, a request which would have ordinarily sent credentials but could not due to cross-partition prefetch causes a prefetch to be abandoned.

A prefetch record's response is the response of the last element of its redirect chain, or null if that list is empty.

The user agent may cancel and discard records from the prefetch records even if they are not expired, e.g., due to resource constraints. Since completed records with expiry times in the past will never be matching prefetch records, they can be removed with no observable consequences.

A prefetch record prefetchRecord matches a URL given a URL url if the following algorithm returns true:
  1. If prefetchRecord’s URL is equal to url, return true.

  2. If prefetchRecord’s response is not null:

    1. Let searchVariance be the result of obtaining a URL search variance given prefetchRecord’s response.

    2. If prefetchRecord’s URL and url are equivalent modulo search variance given searchVariance, return true.

  3. Otherwise, return false.

A prefetch record prefetchRecord is expected to match a URL given a URL url if the following algorithm returns true:
  1. If prefetchRecord matches a URL given url, return true.

  2. If prefetchRecord’s response is null:

    1. Let searchVariance be prefetchRecord’s No-Vary-Search hint.

    2. If prefetchRecord’s URL and url are equivalent modulo search variance given searchVariance, return true.

  3. Otherwise, return false.

To cancel and discard a prefetch record prefetchRecord given a Document document, perform the following steps.
  1. Assert: prefetchRecord is in document’s prefetch records.

  2. Assert: prefetchRecord’s state is not "canceled".

  3. Set prefetchRecord’s state to "canceled".

  4. Abort prefetchRecord’s fetch controller. This will cause any ongoing fetch to be canceled and yield a network error.

  5. Remove prefetchRecord from document’s prefetch records.

This means that even a completed prefetch will not be served from the prefetch buffer. However, if it was part of the same partition as the document which requested it, it might still be stored in the ordinary HTTP cache.
To complete a prefetch record prefetchRecord given Document document, perform the following steps.
  1. Assert: document is fully active.

  2. Let currentTime be the current high resolution time for the relevant global object of document.

  3. Let expiryTime be currentTime + 300000 (i.e., five minutes).

  4. Remove all elements of document’s prefetch records which have the same URL as prefetchRecord and whose state equals "completed".

  5. Set prefetchRecord’s state to "completed" and expiry time to expiryTime.

To find a matching complete prefetch record given a Document document, URL url, and sandboxing flag set sandboxFlags, perform the following steps.
  1. Assert: document is fully active.

  2. Let exactRecord be null.

  3. Let inexactRecord be null.

  4. For each record of document’s prefetch records:

    1. If record’s state is not "completed", then continue.

    2. If record’s sandboxing flag set is empty and sandboxFlags is not empty, then continue.

      Strictly speaking, it would still be possible for this to be valid if sandbox flags have been added to the container since prefetch but those flags would not cause an error due to cross origin opener policy. This is expected to be rare and so isn’t handled.
    3. If record’s URL is equal to url:

      1. Set exactRecord to record.

      2. Break.

    4. If inexactRecord is null and record matches a URL given url:

      1. Set inexactRecord to record.

  5. Let recordToUse be exactRecord if exactRecord is not null, otherwise inexactRecord.

  6. If recordToUse is not null:

    1. Remove recordToUse from document’s prefetch records.

    2. Let currentTime be the current high resolution time for the relevant global object of document.

    3. If recordToUse’s expiry time is less than currentTime, return null.

    4. For each exchangeRecord of recordToUse’s redirect chain:

      1. If conflicting credentials exist for exchangeRecord’s response given document’s node navigable and recordToUse’s source partition key, return null.

      This handles the case where there were no cross-partition credentials initially, but there are now. User agents could use a slightly coarser algorithm, such as monitoring whether the cookies for the URL have been modified at all, or storing a hash, timestamp or revision number.
    5. Return recordToUse.

  7. Return null.

It’s not obvious, but this doesn’t actually require that the prefetch have received the complete body, just the response headers. In particular, a navigation to a prefetched response might nonetheless not load instantaneously.

It might be possible to use cache response headers to determine when a response can be used multiple times, but given the short lifetime of the prefetch buffer it’s unclear whether this is worthwhile.

To wait for a matching prefetch record given a Document document, URL url, and sandboxing flag set sandboxFlags, perform the following steps.
  1. Assert: this is running in parallel.

  2. Let cutoffTime be null.

  3. While true:

    1. Let completeRecord be the result of finding a matching complete prefetch record given document, url, and sandboxFlags.

    2. If completeRecord is not null, return completeRecord.

    3. Let potentialRecords be an empty list.

    4. For each record of document’s prefetch records:

      1. If all of the following are true, then append record to potentialRecords:

    5. If potentialRecords is empty, return null.

    6. Wait until the state of any element of document’s prefetch records changes.

    7. If cutoffTime is null and any element of potentialRecords has a state that is not "ongoing", set cutoffTime to the current high resolution time for the relevant global object of document.

The reasoning for setting the cutoff time after waiting for a prefetch record to finish is to allow for flexibility in selecting a prefetch to serve the navigation while still guaranteeing falling back to a non-prefetched navigation in the case of repeated prefetch failures. We allow blocking on prefetch attempts which started before we see an attempt fail, but we don’t block on subsequent attempts. Notably, this approach: does not finalize the set of prefetches to block on at the start of the navigation; allows a prefetch which started and completed after the navigation started to serve the navigation; avoids the use of a fixed timeout, which would be arbitrary and detrimental to the use of prefetch with slower servers; and blocks on, at most, two nearly-consecutive prefetches before falling back to a conventional navigation.

To create navigation params from a prefetch record given a navigable navigable, a document state documentState, a navigation id navigationId, a NavigationTimingType navTimingType, a request request, a prefetch record record, a target snapshot params targetSnapshotParams, and a source snapshot params sourceSnapshotParams, perform the following steps.
  1. Let responseOrigin be null.

  2. Let responseCOOP be null.

  3. Let coopEnforcementResult be the result of creating a cross-origin opener policy enforcement result for navigation given navigable’s active document and documentState’s initiator origin.

  4. Let finalSandboxFlags be an empty sandboxing flag set.

  5. Let responsePolicyContainer be null.

  6. Let urlList be an empty list.

  7. For each exchangeRecord in record’s redirect chain:

    1. Let response be exchangeRecord’s response.

    2. Append response’s URL to urlList.

    3. Set responsePolicyContainer to the result of creating a policy container from a fetch response given response and request’s reserved client.

    4. Set finalSandboxFlags to the union of targetSnapshotParams’s sandboxing flags and responsePolicyContainer’s CSP list's CSP-derived sandboxing flags.

    5. Set responseOrigin to the result of determining the origin given response’s URL, finalSandboxFlags, documentState’s initiator origin, and null.

    6. If navigable is a top-level traversable, then:

      1. Set responseCOOP to the result of obtaining a cross-origin opener policy given response and request’s reserved client.

      2. Assert: If finalSandboxFlags is not empty, then responseCOOP’s value is "unsafe-none".

        This is guaranteed since the sandboxing flags of the document cannot change to become non-empty since this was prefetched, and the check was not done for a different window. If this changes, this will need to be able to handle failure of this check.

      3. Set coopEnforcementResult to the result of enforcing a response’s cross-origin opener policy given navigable’s active browsing context, response’s URL, responseOrigin, responseCOOP, coopEnforcementResult, and exchangeRecord’s request's referrer.

  8. Set request’s URL list to urlList.

  9. Let resultPolicyContainer be the result of determining navigation params policy container given record’s response's URL, documentState’s history policy container, sourceSnapshotParams’s source policy container, null, and responsePolicyContainer.

  10. Return a new navigation params, with:

    id

    navigationId

    request

    request

    response

    record’s response

    origin

    responseOrigin

    policy container

    resultPolicyContainer

    final sandboxing flag set

    finalSandboxFlags

    cross-origin opener policy

    responseCOOP

    COOP enforcement result

    coopEnforcementResult

    reserved environment

    request’s reserved client

    navigable

    navigable

    navigation timing type

    navTimingType

    fetch controller

    record’s fetch controller

    commit early hints

    null

    delivery type

    "navigational-prefetch"

    This implies that early hints delivered for prefetched documents won’t be processed. This could be revised in the future if it is common for there to be useful resource hints which are not repeated in the main response headers. Since prefetches aren’t currently served until the response headers arrive, early hints would not be processed any earlier than ordinary response headers. It might be possible for a future specification to allow early hints found during prefetching to be used in some way.

A prefetch IP anonymization policy is either null or a cross-origin prefetch IP anonymization policy.

A cross-origin prefetch IP anonymization policy has an origin, which is an origin.

A prefetch IP anonymization policy policy requires anonymity for request request if the following steps return true:
  1. If policy is a cross-origin prefetch IP anonymization policy:

    1. If request’s URL's origin is the same as policy’s origin, then return false.

    2. Return true.

  2. Assert: policy is null.

  3. Return false.

2. HTML Patches

This section contains patches to [HTML].

Add an additional item to navigation params as follows:

delivery type

a string (corresponding to PerformanceResourceTiming delivery type)

Update all creation sites to supply an empty string, except for any in this document which supply a different value (in create navigation params from a prefetch record).


This is extracted from create navigation params by fetching.

To create a reserved client given a navigable navigable, a URL url and an opaque origin or null isolationOrigin:

  1. Let topLevelCreationURL be url.

  2. Let topLevelOrigin be null.

  3. If isolationOrigin is not null, then:

    1. Set topLevelCreationURL to about:blank.

    2. Set topLevelOrigin to isolationOrigin.

  4. Otherwise, if navigable is not a top-level traversable, then:

    1. Let parentEnvironment be navigable’s parent's active document's relevant settings object.

    2. Set topLevelCreationURL to parentEnvironment’s top-level creation URL.

    3. Set topLevelOrigin to parentEnvironment’s top-level origin.

  5. Return a new environment whose id is a unique opaque string, target browsing context is navigable’s active browsing context, creation URL is url, top-level creation URL is topLevelCreationURL, and top-level origin is topLevelOrigin.


This is extracted from create navigation params by fetching.

To create a cross-origin opener policy enforcement result for navigation given a Document activeDocument and an origin initiatorOrigin, return a new cross-origin opener policy enforcement result with

url

activeDocument’s URL

origin

activeDocument’s origin

cross-origin opener policy

activeDocument’s cross-origin opener policy

current context is navigation source

true if activeDocument’s origin is same origin with initiatorOrigin; otherwise false


This is extracted from create navigation params by fetching.

To create a navigation request given a session history entry entry, an environment settings object fetchClient, a navigable container or null container, and a boolean hasTransientActivation, perform the following steps.

  1. Let documentResource be entry’s document state's resource.

  2. Let request be a new request, with

    url

    entry’s URL

    policy container

    entry’s document state's history policy container

    client

    fetchClient

    destination

    "document"

    credentials mode

    "include"

    use-URL-credentials flag

    set

    redirect mode

    "manual"

    mode

    "navigate"

    referrer

    entry’s document state's request referrer

    referrer policy

    entry’s document state's request referrer policy

  3. If documentResource is a POST resource, then:

    1. Set request’s method to `POST`.

    2. Set request’s body to documentResource’s request body.

    3. Set `Content-Type` to documentResource’s request content-type in request’s header list.

  4. If entry’s document state's reload pending is true, then set request’s reload-navigation flag.

  5. Otherwise, if entry’s document state's ever populated is true, then set request’s history-navigation flag.

  6. If hasTransientActivation is true, then set request’s user-activation to true.

  7. If container is non-null:

    1. If container has a browsing context scope origin, then set request’s origin to that browsing context scope origin.

    2. Set request’s destination and initiator type to container’s local name.

  8. Return request.


In attempt to populate the history entry’s document, replace the step which invokes create navigation params by fetching with the following:
  1. Otherwise, if both of the following are true:

    then:

    1. Let request be the result of creating a navigation request given entry, sourceSnapshotParams’s fetch client, navigable’s container, and sourceSnapshotParams’s has transient activation.

    2. Set request’s replaces client id to navigable’s active document's relevant settings object's id.

    3. Let prefetchRecord be the result of waiting for a matching prefetch record given navigable’s active document, entry’s URL, and targetSnapshotParams’s sandboxing flags.

    4. If documentResource is null and prefetchRecord is not null:

      1. Set navigationParams to the result of creating navigation params from a prefetch record given navigable, entry’s document state, navigationId, navTimingType, request, prefetchRecord, targetSnapshotParams, and sourceSnapshotParams.

      2. Copy prefetch cookies given prefetchRecord’s isolated partition key and navigationParams’s reserved environment.

        This copy is complete before continuing, in the sense that subresource fetches, document.cookie, etc. can observe the cookies. If the prefetch never reached a cross-site URL, there will be no cookies to copy.
    5. Otherwise:

      1. Let coopEnforcementResult be the result of creating a cross-origin opener policy enforcement result for navigation given navigable’s active document and entry’s document state's initiator origin.

      2. Set navigationParams to the result of creating navigation params by fetching given request, entry, coopEnforcementResult, navigable, sourceSnapshotParams, targetSnapshotParams, cspNavigationType, navigationId, and navTimingType.


This is an update of the existing create navigation params by fetching algorithm.

To create navigation params by fetching given a request request, a session history entry entry, a cross-origin opener policy enforcement result coopEnforcementResult, a navigable navigable, a source snapshot params sourceSnapshotParams, a target snapshot params targetSnapshotParams, a string cspNavigationType, a navigation ID or null navigationId, a NavigationTimingType navTimingType, and an optional prefetch record prefetchRecord, perform the following steps.

  1. Assert: this is running in parallel.

  2. Assert: request’s URL is entry’s URL.

  3. Assert: request’s mode is "navigate".

  4. Assert: request’s redirect mode is "manual".

  5. Assert: request’s reserved client is null.

  6. Let response be null.

  7. Let responseOrigin be null.

  8. Let fetchController be null.

  9. Let finalSandboxFlags be an empty sandboxing flag set.

  10. Let responsePolicyContainer be null.

  11. Let responseCOOP be a new cross-origin opener policy.

  12. Let locationURL be null.

  13. Let currentURL be request’s current URL.

  14. Let commitEarlyHints be null.

  15. Let isolationOrigin be null.

  16. If prefetchRecord was given:

    1. Let isolationSite be prefetchRecord’s isolated partition key[0].

    2. Assert: isolationSite is an opaque origin.

    3. Set isolationOrigin to isolationSite.

  17. While true:

    1. If request’s reserved client is not null and currentURL’s origin is not the same as request’s reserved client's creation URL's origin, then:

      1. Run the environment discarding steps for request’s reserved client.

      2. Set request’s reserved client to null.

      3. Set commitEarlyHints to null.

    2. If request’s reserved client is null, then set request’s reserved client to the result of creating a reserved client given navigable, currentURL and isolationOrigin.

    3. If the result of should navigation request of type be blocked by Content Security Policy? given request and cspNavigationType is "Blocked", then set response to a network error and break. [CSP]

    4. If prefetchRecord was given, then:

      1. Let purpose be a List containing the Token prefetch.

      2. If prefetchRecord’s anonymization policy requires anonymity for request, then:

        1. Add a parameter whose key is "anonymous-client-ip" and whose value is true to the prefetch token in purpose.

        2. The user agent must use a connection which anonymizes the client IP address (e.g., using a proxy) when fetching request, or set response to a network error and break.

          At the moment, how IP anonymization is achieved is handwaved. This will probably be done in an implementation-defined manner using some kind of proxy or relay. Ideally this would be plumbed down to obtain a connection, and possibly even the mechanism could be further standardized.

      3. Set a structured field value given (`Sec-Purpose`, purpose) in request’s header list.

        Implementations might also send vendor-specific headers, like Chromium’s `Purpose`/`prefetch`, Mozilla’s `X-moz`/`prefetch`, and WebKit’s `X-Purpose`/`preview`, for compatibility with existing server software. Over time we hope implementers and server software authors will adopt this common header.
      4. If request’s current URL is not potentially trustworthy, then set response to a network error and break.

        This is intended to both reduce the likelihood of prefetch traffic being visible to an on-path attacker, and to encourage the use of cryptographic schemes over public networks.
      5. Let proposedPartitionKey be the result of determining the network partition key given request’s reserved client.

      6. If proposedPartitionKey is not equal to prefetchRecord’s source partition key and request’s referrer policy is not in the list of sufficiently strict speculative navigation referrer policies, then set response to a network error and break.

        In practice, this means that cross-site prefetches will abandon rather than expose more information about the referrer URL than the origin.
      7. If request cannot be fetched given prefetchRecord’s anonymization policy for an implementation-defined reason, then set response to a network error and break.

        This explicitly acknowledges that implementations might have additional restrictions. For instance, anonymized traffic might not be possible to some hosts, such as those that are not publicly routable and those that have traffic advice declining private prefetch traffic.
      8. Append a new exchange record whose request is request and response is null to prefetchRecord’s redirect chain.

    5. Set response to null.

    6. If fetchController is null, then set fetchController to the result of fetching request, with processEarlyHintsResponse set to processEarlyHintsResponse as defined below, processResponse set to processResponse as defined below, and useParallelQueue set to true.

      Let processEarlyHintsResponse be the following algorithm given a response earlyResponse:

      1. If prefetchRecord was given and commitEarlyHints is null, then set commitEarlyHints to the result of processing early hint headers given earlyResponse and request’s reserved client.

      Let processResponse be the following algorithm given a response fetchedResponse:

      1. Set response to fetchedResponse.

    7. Otherwise, process the next manual redirect for fetchController.

    8. Wait until either response is non-null, or navigable’s ongoing navigation changes to no longer equal navigationId.

      If the latter condition occurs, then abort fetchController, and return. Otherwise, proceed onward. This aborts prefetches the moment a navigation starts, which might not be desirable.

    9. If request’s body is null, then set entry’s document state's resource to null.

    10. Set responsePolicyContainer to the result of creating a policy container from a fetch response given response and request’s reserved client.

    11. Set finalSandboxFlags to the union of targetSnapshotParams’s sandboxing flags and responsePolicyContainer’s CSP list's CSP-derived sandboxing flags.

    12. Set responseOrigin to the result of determining the origin given response’s URL, finalSandboxFlags, entry’s document state's initiator origin, and null.

    13. If navigable is a top-level traversable, then:

      1. Set responseCOOP to the result of obtaining a cross-origin opener policy given response and request’s reserved client.

      2. If prefetchRecord was given, then set responseCOOP’s reporting endpoint and report-only reporting endpoint to null. This allows COOP violation reports to be suppressed until the prefetch is used.

      3. Set coopEnforcementResult to the result of enforcing a response’s cross-origin opener policy given navigable’s active browsing context, request’s URL, responseOrigin, responseCOOP, coopEnforcementResult, and request’s referrer.

      4. If finalSandboxFlags is not empty and responseCOOP’s value is not "unsafe-none", then set response to an appropriate network error and break.

    14. If response is not a network error, navigable is a child navigable, and the result of performing a cross-origin resource policy check with navigable’s container document's origin, navigable’s container document's relevant settings object, request’s destination, response, and true is blocked, then set response to a network error and break.

    15. If prefetchRecord was given, then:

      1. Update the response for its redirect chain given request and response.

      2. If conflicting credentials exist for response given navigable and prefetchRecord’s source partition key, then set prefetchRecord’s had conflicting credentials to true.

        This does not immediately abort the prefetch or stop following redirects, because doing so might reveal whether or not the user has stored state outside the current partition, before the user navigates. Instead, the prefetch continues as though there were no conflicting credentials, except that the prefetch cannot actually be used. User agents might wish to report a warning to the console or otherwise inform authors that this has happened.

    16. Set locationURL to response’s location URL given currentURL’s fragment.

    17. If locationURL is failure or null, then break.

    18. Assert: locationURL is a URL.

    19. Set entry’s serialized state to StructuredSerializeForStorage(null).

    20. Let oldDocState be entry’s document state.

    21. Set entry’s document state to a new document state, with:

      history policy container

      a clone of oldDocState’s history policy container

      request referrer

      oldDocState’s request referrer

      request referrer policy

      oldDocState’s request referrer policy

      origin

      oldDocState’s origin

      resource

      oldDocState’s resource

      ever populated

      oldDocState’s ever populated

      navigable target name

      oldDocState’s navigable target name

    22. If locationURL’s scheme is not an HTTP(S) scheme, then:

      1. Set entry’s document state's resource to null.

      2. Break.

    23. Set currentURL to locationURL.

    24. Set entry’s URL to currentURL.

  18. If locationURL is a URL whose scheme is not a fetch scheme, then return a new non-fetch scheme navigation params, with:

    initiator origin

    request’s current URL's origin

  19. If any of the following are true:

    then return null.
  20. Let resultPolicyContainer be the result of determining navigation params policy container given response’s URL, entry’s document state's history policy container, sourceSnapshotParams’s source policy container, null, and responsePolicyContainer.

  21. Return a new navigation params, with:

    id

    navigationId

    request

    request

    response

    response

    origin

    responseOrigin

    policy container

    resultPolicyContainer

    final sandboxing flag set

    finalSandboxFlags

    cross-origin opener policy

    responseCOOP

    COOP enforcement result

    coopEnforcementResult

    reserved environment

    request’s reserved client

    navigable

    navigable

    navigation timing type

    navTimingType

    fetch controller

    fetchController

    commit early hints

    commitEarlyHints

Update the steps to create and initialize a Document object to add an additional argument to create the navigation timing entry, as follows: navigationParams’s delivery type.

This section contains patches to [NAVIGATION-TIMING].

Add an additional parameter to create the navigation timing entry, which is a string deliveryType, and pass it as an additional argument to setup the resource timing entry.

4. Prefetch algorithms

Check Service Worker integration

The list of sufficiently strict speculative navigation referrer policies is a list containing the following: "", "strict-origin-when-cross-origin", "strict-origin", "same-origin", "no-referrer".

To prefetch given a Document document and a prefetch record prefetchRecord, perform the following steps.
  1. Let sourceSnapshotParams be the result of snapshotting source snapshot params given document.

  2. Let targetSnapshotParams be the result of snapshotting target snapshot params given document’s node navigable.

  3. Set prefetchRecord’s source partition key to the result of determining the network partition key given document’s relevant settings object.

  4. Assert: prefetchRecord’s URL's scheme is an HTTP(S) scheme.

  5. Append prefetchRecord to document’s prefetch records

  6. Set prefetchRecord’s start time to the current high resolution time for the relevant global object of document.

  7. Set prefetchRecord’s sandboxing flag set to the result of determining the creation sandboxing flags for document’s browsing context given document’s node navigable's container.

  8. Let referrerPolicy be prefetchRecord’s referrer policy if prefetchRecord’s referrer policy is not the empty string, and document’s policy container's referrer policy otherwise.

  9. Let documentState be a new document state with

    request referrer policy

    referrerPolicy

    initiator origin

    document’s origin

  10. Let entry be a new session history entry with

    URL

    prefetchRecord’s URL

    document state

    documentState

  11. Let request be the result of creating a navigation request given entry, document’s relevant settings object, document’s node navigable's container, and false.

  12. Let coopEnforcementResult be the result of creating a cross-origin opener policy enforcement result for navigation given document and document’s origin.

  13. Let global be document’s relevant global object.

  14. In parallel:

    1. Let navigationParams be the result of creating navigation params by fetching given request, entry, coopEnforcementResult, document’s node navigable, sourceSnapshotParams, targetSnapshotParams, "other", null (navigationId), "navigate", and prefetchRecord prefetchRecord.

    2. If navigationParams’s response does not support prefetch, then set navigationParams to null.

    3. If prefetchRecord’s had conflicting credentials is true, then set navigationParams to null.

      This means that if any cross-partition origin along the redirect chain had credentials (and did not override this behavior using `Supports-Loading-Mode`), the prefetch is discarded. This reduces the chance of the user observing a logged-out page when they are logged in.
    4. Queue a global task on the networking task source, given global, to:

      1. If navigationParams is not a navigation params, then cancel and discard prefetchRecord given document and abort these steps.

      2. Assert: navigationParams’s response is the response of prefetchRecord’s redirect chain's last element.

      3. Complete prefetchRecord given document.

A response response supports prefetch if the following steps return true:
  1. Let status be response’s status.

  2. Assert: status is not a redirect status.

  3. If status is not an ok status, then return false.

    In particular, this means that error responses aren’t stored and will be retried when a navigation occurs. This increases the likelihood of navigations succeeding if the error was transient or due to the request being for prefetch. It also gives server software a simple way to refuse to handle requests which carry a `Sec-Purpose` request header indicating prefetch.
  4. Return true.

A future draft of this specification is expected to provide a way for responses to be marked as eligible or ineligible explicitly.

5. Cookies

[COOKIES] defines "cookies" which can be set using the `Set-Cookie` response header field. Because the "uncredentialed" partitioning scheme forces a separate network partition key to be used, it’s necessary to copy these cookies into the ordinary partition as though they had been received at the time of navigation.

Cache, though not necessary for correctness, would be useful to copy, too.
Authentication entries can be acquired while cross-site, though this seems likely to be rare. (For instance, a site might redirect to a URL which contains URL credentials.) It might be reasonable to copy these, too, though it’s unclear whether we actually want to encourage this.
To copy prefetch cookies given a network partition key isolatedPartitionKey and an environment environment, perform the following steps.
Though formally there is only one cookie store in [COOKIES], some browsers partition cookie stores so as to separate, for example, cookies with the same domain when loaded with different top-level sites. See Client-Side Storage Partitioning (Privacy CG).
  1. Let isolatedCookieStore be the cookie store associated with isolatedPartitionKey.

  2. For each cookie cookie in isolatedCookieStore.

    1. Remove cookie from isolatedCookieStore.

    2. A user agent may ignore a cookie in its entirety. If so, continue.

      This is consistent with [COOKIES] expressly permitting this when receiving a cookie.
    3. Let topLevelSite be null.

    4. If environment’s target browsing context is a top-level browsing context:

      1. Set topLevelSite to the result of obtaining a site given tuple origin ("https", cookie’s domain, null, null).

        The use of the "https" scheme and null port here is arbitrary because cookies are visible across schemes and ports, in contrast to the usual same origin policy. The user agent’s choice of associated cookie store, therefore, cannot be sensitive to either.
      When performing a prefetch in a top-level browsing context, the request (including all redirects) is preparing for a top-level navigation. environment’s top-level site changes as redirects are followed, and since a redirect might be cross-site, environment’s top-level site might have changed since a given cookie was received. However, since the navigation is top-level, the origin delivering the cookie would have been the top-level site at the time. Since the cookie’s domain has to be same-site with an origin delivering it, the cookie’s domain can be used to determine the correct top-level site.
    5. Otherwise:

      1. Let topLevelOrigin be environment’s top-level origin.

      2. If topLevelOrigin is null, then set topLevelOrigin to environment’s top-level creation URL's origin.

      3. Assert: topLevelOrigin is an origin.

      4. Set topLevelSite be the result of obtaining a site given topLevelOrigin.

      When performing a prefetch in a child navigable, the top-level site is determined by the top-level traversable that contains it. Since that doesn’t change as redirects are followed, environment can be used to establish the top-level site.
    6. Let secondKey be null or an implementation-defined value.

      secondKey is expected to match the value it would have had if this response had been processed as part of an ordinary navigation in environment’s target browsing context.
    7. Let destinationPartitionKey be (topLevelSite, secondKey).

    8. Let cookieStore be the cookie store associated with destinationPartitionKey.

    9. Let newCookie be a copy of cookie.

    10. Set newCookie’s creation-time and last-access-time to the current date and time.

    11. If cookieStore contains a cookie existingCookie with the same name, domain and path as the newly created cookie:

      1. Set the creation-time of newCookie to existingCookie’s creation-time.

      2. Remove existingCookie from cookieStore.

    12. Insert newCookie into cookieStore.

      This remove-and-insert pattern is consistent with what happens when receiving a cookie.

6. The Sec-Purpose HTTP request header

The `Sec-Purpose` HTTP request header specifies that the request serves one or more purposes other than requesting the resource for immediate use by the user.

The header field is an [RFC8941] Structured Header whose value must be a List. Its ABNF is:

Sec-Purpose = sf-list

It may contain an Item member which is the Token "prefetch". If so, this indicates the request’s purpose is to download a resource it is anticipated will be fetched shortly.

TODO: Are there normative implications of this that should be specified here?

The following parameters are defined for the "prefetch" token:

7. Security considerations

See Security considerations (Speculation Rules).

8. Privacy considerations

See Privacy considerations (Speculation Rules).

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[COOKIES]
A. Barth. HTTP State Management Mechanism. April 2011. Proposed Standard. URL: https://httpwg.org/specs/rfc6265.html
[CSP]
Mike West; Antonio Sartori. Content Security Policy Level 3. URL: https://w3c.github.io/webappsec-csp/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[NAVIGATION-TIMING-2]
Yoav Weiss; Noam Rosenthal. Navigation Timing Level 2. URL: https://w3c.github.io/navigation-timing/
[REFERRER-POLICY]
Jochen Eisinger; Emily Stark. Referrer Policy. URL: https://w3c.github.io/webappsec-referrer-policy/
[RESOURCE-TIMING]
Yoav Weiss; Noam Rosenthal. Resource Timing. URL: https://w3c.github.io/resource-timing/
[RFC8941]
M. Nottingham; P-H. Kamp. Structured Field Values for HTTP. February 2021. Proposed Standard. URL: https://httpwg.org/specs/rfc8941.html
[SECURE-CONTEXTS]
Mike West. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/

Informative References

[CONSOLE]
Dominic Farolino; Robert Kowalski; Terin Stock. Console Standard. Living Standard. URL: https://console.spec.whatwg.org/
[NAVIGATION-TIMING]
Zhiheng Wang. Navigation Timing. 17 December 2012. REC. URL: https://www.w3.org/TR/navigation-timing/
[PRERENDERING-REVAMPED]
Prerendering Revamped. Draft Community Group Report. URL: https://wicg.github.io/nav-speculation/prerendering.html

Issues Index

It might be possible to use cache response headers to determine when a response can be used multiple times, but given the short lifetime of the prefetch buffer it’s unclear whether this is worthwhile.
At the moment, how IP anonymization is achieved is handwaved. This will probably be done in an implementation-defined manner using some kind of proxy or relay. Ideally this would be plumbed down to obtain a connection, and possibly even the mechanism could be further standardized.
This aborts prefetches the moment a navigation starts, which might not be desirable.
Check Service Worker integration
Cache, though not necessary for correctness, would be useful to copy, too.
Authentication entries can be acquired while cross-site, though this seems likely to be rare. (For instance, a site might redirect to a URL which contains URL credentials.) It might be reasonable to copy these, too, though it’s unclear whether we actually want to encourage this.
TODO: Are there normative implications of this that should be specified here?