This specification defines the preload keyword that may be used with [link] elements. This keyword provides a declarative fetch primitive that initiates an early fetch and separates fetching from resource execution.

This is a work in progress and may change without any notices.

Implementers SHOULD be aware that this document is not stable. Implementers who are not taking part in the discussions are likely to find the specification changing out from under them in incompatible ways. Vendors interested in implementing this document before it eventually reaches the Candidate Recommendation stage SHOULD join the mailing lists below and take part in the discussions.

Introduction

Many applications require fine-grained control over when resources are fetched, processed, and applied to the document. For example, the loading and processing of some resources may be deferred by the application to reduce resource contention and improve performance of the initial load. This behavior is typically achieved by moving resource fetching into custom resource loading logic defined by the application - i.e. resource fetches are initiated via injected elements, or via `XMLHttpRequest`, when particular application conditions are met.

However, there are also cases where some resources need to be fetched as early as possible, but their processing and execution logic is subject to application-specific requirements - e.g. dependency management, conditional loading, ordering guarantees, and so on. Currently, it is not possible to deliver this behavior without a performance penalty.

The preload keyword on [link] elements provides a declarative fetch primitive that addresses the above use case of initiating an early fetch and separating fetching from resource execution. As such, preload keyword serves as a low-level primitive that enables applications to build custom resource loading and execution behaviors without hiding resources from the user agent and incurring delayed resource fetching penalties.

For example, the application can use the preload keyword to initiate early, high-priority, and non-render-blocking fetch of a CSS resource that can then be applied by the application at appropriate time:

<!-- preload stylesheet resource via declarative markup -->
<link rel="preload" href="/styles/other.css" as="style">

<!-- or, preload stylesheet resource via JavaScript -->
<script>
var res = document.createElement("link");
res.rel = "preload";
res.as = "style";
res.href = "styles/other.css";
document.head.appendChild(res);
</script>

As above examples illustrate, the resource can be specified via declarative markup, Link HTTP header ([[!RFC5988]]), or scheduled via JavaScript. See use cases section for more hands-on examples of how and where preload can be used.

Link type "preload"

The preload keyword may be used with [link] elements. This keyword creates an [external resource link] (preload link) that is used to declare a resource and its fetch properties. [[!HTML5]]

If the preload keyword is used as an optimization to initiate earlier fetch then no additional feature detection checks are necessary: browsers that support preload will initiate earlier fetch, and those that do not will ignore it and fetch the resource as previously. Otherwise, if the application intends to rely on preload to fetch the resource, then it can [execute a feature detection check][feature-detect] to verify that it is supported.

Both `prefetch` ([[RESOURCE-HINTS]]) and `preload` declare a resource and its fetch properties, but differ in how and when the resource is fetched by the user agent: `prefetch` is an optional and low-priority fetch for a resource that might be used by a subsequent navigation; `preload` is a mandatory and high-priority fetch for a resource that is necessary for the current navigation. Developers should use each one accordingly to minimize resource contention and optimize load performance.

Processing

The appropriate times to obtain the preload resource are:

The user agent SHOULD abort the current request if the `href` attribute of the [link] element of a preload link is changed, removed, or its value is set to an empty string.

To obtain the preload resource, the user agent must run the following steps:

  1. If the `href` attribute's value is missing or is the empty string, then abort these steps.
  2. [Resolve] the [URL] (absolute url) given by the `href` attribute, relative to the element.
  3. If the previous step fails, then abort these steps.
  4. Validate the request destination given by the `as` attribute. If the attribute is omitted, then initialize it to the empty string. If the provided value is not a [valid request destination], then [queue a task] to [fire an event] named `error` at the [link] element and abort these steps.[[!FETCH]]
  5. If the `type` attribute's value is missing or is the empty string, then continue to the next step. Otherwise, If the `type` attribute's value is not a [parsable MIME type] or is an [unsupported MIME type], then abort these steps.
  6. If the `media` attribute's value is missing or is the empty string, then continue to the next step. Otherwise, if the `media` attribute's value is not a [valid media query list] that does [match the environment], then abort these steps.
  7. Do a potentially CORS-enabled fetch of the resulting absolute URL, with the mode being the current state of the element's [crossorigin] content attribute, the origin being the [origin] of the [link] element's [node document], destination set to request destination, and the default origin behavior set to taint.

The preload link element MUST NOT [delay the load event] of the element's [node document].

The fetch initiated by a preload link is subject to relevant CSP policies determined by the value of the `as` attribute - e.g. when set to `image` the fetch is subject to `image-src`. Similarly, the value of the `as` attribute is used to initialize request headers and priority. If the `as` attribute is omitted, the request will be initialized with similar processing and request properties as one initiated by `XMLHttpRequest`.

Once a preload resource has been obtained, the user agent must run these steps:

  1. If the load was successful, [queue a task] to [fire an event] named `load` at the [link] element. Otherwise, [queue a task] to [fire a simple event] named `error` at the [link] element.
  2. Add request to fetch group's response cache.

    In addition to the HTTP cache, all browser implementations provide one or more levels of additional caches, which sometimes live before the HTTP cache (e.g. HTTP/2 server push responses are typically not commited to HTTP cache until a client request is made), and after the HTTP cache (e.g. in-process memory caches). These caches are not defined today and need to be defined in Fetch API— see [related discussion](https://github.com/whatwg/fetch/issues/354).

    Conceptually, a preloaded response ought to be committed to the HTTP cache, as it is initiated by the client, and also be available in the memory cache and be re-usable at least once within the lifetime of a fetch group.

The user agent MUST NOT automatically execute or apply the resource against the current page context.

For example, if a JavaScript resource is fetched via a preload link and the response contains a `no-cache` directive, the fetched response is retained by the user agent and is made immediately available when fetched with a matching same navigation request at a later time - e.g. via a `script` tag or other means. This ensures that the user agent does not incur an unnecessary revalidation, or a duplicate download, between the initial resource fetch initiated via the preload link and a later fetch requesting the same resource.

Link element interface extensions

partial interface HTMLLinkElement {
  [CEReactions] attribute DOMString as;
};

The as attribute's value MUST be a valid [request destination]. If the provided value is omitted, the value is initialized to the empty string.

The user agent MUST set the [request destination] provided by `as`, as well as the corresponding [request type] and [request initiator]. This is necessary to guarantee correct prioritization, request matching, application of the correct [[!CSP3]] policy, and setting of the appropriate `Accept` request header.

When the resource is declared via the `Link` header field ([[!RFC5988]]), the resource's `as` attribute is defined via the `as` link-extension target attribute. ([[!RFC5988]] section 5.4)

Example directives to preload a resource that will be consumed by...

consumer Preload directive
<audio>, <video> <link rel=preload as=media href=...>
<script>, Worker's importScripts <link rel=preload as=script href=...>
<link rel=stylesheet>, CSS @import <link rel=preload as=style href=...>
CSS @font-face <link rel=preload as=font href=...>
<img>, <picture>, srcset, imageset <link rel=preload as=image href=...>
SVG's <image>, CSS *-image <link rel=preload as=image href=...>
XHR, fetch <link rel=preload href=...>
Worker, SharedWorker <link rel=preload as=worker href=...>
<embed> <link rel=preload as=embed href=...>
<object> <link rel=preload as=object href=...>
<iframe>, <frame> <link rel=preload as=document href=...>

Server Push (HTTP/2)

HTTP/2 ([[!RFC7540]]) allows a server to pre-emptively send ("push") responses to the client. A pushed response is semantically equivalent to a server responding to a request and, similar to a preloaded response, is retained by the user agent and executed by the application when matched with a request initiated by the application. As such, from an application perspective, there is no difference between consuming a preload or a server push response.

The server MAY initiate server push for preload link resources defined by the application for which it is [authoritative][]. Initiating server push eliminates the request roundtrip between client and server for the declared preload link resource. Optionally, if the use of server push is not desired for a resource declared via the `Link` header field ([[!RFC5988]]), the developer MAY provide an opt-out signal to the server via the `nopush` target attribute ([[!RFC5988]] section 5.4). For example:

The above example indicates to an HTTP/2 push capable server that `/app/style.css` should not be pushed (e.g. the origin may have additional information indicating that it may already be in cache), while `/app/script.js` should be considered as a candidate for server push.

Initiating server push for a preload link is an optional optimization. For example, the server might omit initiating push if it believes that the response is available in the client's cache: the client will process the preload directive, check the relevant caches, and initiate the request to the server if the resource is missing. Alternatively, the server might omit initiating push due to operational concerns, such as available server resources or other criteria. Finally, the use of server push is subject to negotiated HTTP/2 connection settings: the client may limit or outright disable the use of server push. Applications cannot rely on the availability and use of server push.

There is only one class of product that can claim conformance to this specification: a user agent.

Use cases

Early fetch of critical resources

Preload parsers are used by most user agents to initiate early resource fetches while the main document parser is blocked due to a blocking script. However, the preload parsers do not execute JavaScript, and typically only perform a shallow parse of CSS, which means that the fetch of resources specified within JavaScript and CSS is delayed until the relevant document parser is able to process the resource declaration.

In effect, most resources declarations specified within JavaScript and CSS are "hidden" from the speculative parsers and incur a performance penalty. To address this, the application can use a preload link to declaratively specify which resources the user agent must fetch early to improve page performance:

Above markup initiates four resource fetches: a font resource, a stylesheet, an unknown resource type from another origin, and a font resource from another origin. Each fetch is initialized with appropriate request headers and priority - the unknown type is equivalent to a fetch initiated `XMLHttpRequest` request. Further, these requests do not block the parser or the load event.

Preload links for CORS enabled resources, such as fonts or images with a `crossorigin` attribute, must also include a `crossorigin` attribute, in order for the resource to be properly used.

Early fetch and application defined execution

The preload link can be used by the application to initiate early fetch of one or more resources, as well as to provide custom logic for when and how each response should be applied to the document. The application may:

The preload link provides a low-level and content-type agnostic primitive that enables applications to build custom resource loading and execution behaviors without incurring the penalty of delayed resource loading.

For example, preload link enables the application to provide `async` and `defer` like semantics, which are only available on `script` elements today, but for any content-type: applying the resource immediately after it is available provides `async` functionality, whereas adding some ordering logic enables `defer` functionality. Further, this behavior can be defined across a mix of content-types - the application is in full control over when and how each resource is applied.

<script>
  function preloadFinished(e) { ... }
  function preloadError(e)  { ... }
</script>
<!-- listen for load and error events -->
<link rel="preload" href="app.js" as="script" onload="preloadFinished()" onerror="preloadError()">

By decoupling resource fetching from execution, the preload link provides a future-proof primitive for building performant application specific resource loading strategies.

Developer, server, and proxy-initiated fetching

The preload link can be specified by the developer, or be automatically generated by the application server or an optimization proxy (e.g. a CDN).

  <link rel="preload" href="//example.com/widget.html" as="document">
  <script>
  var res = document.createElement("link");
  res.rel = "preload";
  res.as = "document";
  res.href = "/other/widget.html";
  document.head.appendChild(res);
  </script>

IANA Considerations

The link relation type below has been registered by IANA per Section 6.2.1 of [[!RFC5988]]:

Relation Name:
preload
Description:
Refers to a resource that should be loaded early in the processing of the link's context, without blocking rendering.
Reference:
W3C Preload Specification (https://www.w3.org/TR/preload/)
Note:
Additional target attributes establish the detailed fetch properties of the link.

Privacy and Security

Preload is a declarative fetch primitive that initiates early fetch of resources and separates fetching from resource execution. In effect, it is conceptually similar to initiating a scripted fetch for a resource, but with additional constraints and benefits:

The site authors are encouraged to take the necessary precautions and specify the relevant [[!CSP3]], [[!MIXED-CONTENT]], and [[!REFERRER-POLICY]] rules, such that the browser can enforce them when initiating the preload request. Additionally, if preload directives are provided via the `Link` HTTP response header, then the relevant policies should also be delivered as an HTTP response header - e.g. see Processing Complications for CSP.

Acknowledgments

This document reuses text from the [[!HTML]] specification, edited by Ian Hickson, as permitted by the license of that specification.

[link]: https://html.spec.whatwg.org/multipage/semantics.html#the-link-element [external resource link]: https://html.spec.whatwg.org/multipage/semantics.html#external-resource-link [inserted into a document]: https://www.w3.org/TR/html51/infrastructure.html#document-inserted-into-the-document [in a document]: https://html.spec.whatwg.org/multipage/infrastructure.html#in-a-document [Content-Type metadata]: https://html.spec.whatwg.org/multipage/infrastructure.html#content-type [valid media query list]: https://html.spec.whatwg.org/multipage/infrastructure.html#valid-media-query-list [match the environment]: https://html.spec.whatwg.org/multipage/infrastructure.html#matches-the-environment [resolve]: https://html.spec.whatwg.org/multipage/infrastructure.html#resolve-a-url [url]: https://html.spec.whatwg.org/multipage/infrastructure.html#url [request destination]: https://fetch.spec.whatwg.org/#concept-request-destination [request type]: https://fetch.spec.whatwg.org/#concept-request-type [request initiator]: https://fetch.spec.whatwg.org/#concept-request-initiator [crossorigin]: https://html.spec.whatwg.org/multipage/semantics.html#attr-link-crossorigin [origin]: https://html.spec.whatwg.org/multipage/browsers.html#origin-2 [node document]: https://dom.spec.whatwg.org/#concept-node-document [delay the load event]: https://html.spec.whatwg.org/multipage/syntax.html#delay-the-load-event [queue a task]: https://html.spec.whatwg.org/multipage/webappapis.html#queue-a-task [fire an event]: https://dom.spec.whatwg.org/#concept-event-fire [feature-detect]: https://gist.github.com/igrigorik/a02f2359f3bc50ca7a9c [authoritative]: https://httpwg.github.io/specs/rfc7540.html#authority [unsupported MIME type]: https://mimesniff.spec.whatwg.org/#supported-by-the-user-agent [parsable MIME type]: https://mimesniff.spec.whatwg.org/#parsable-mime-type