matrix-public-archive/server/lib/set-headers-to-preload-asse...

77 lines
3.6 KiB
JavaScript
Raw Normal View History

'use strict';
const assert = require('assert');
const getDependenciesForEntryPointName = require('../lib/get-dependencies-for-entry-point-name');
// Set some preload link headers which we can use with Cloudflare to turn into 103 early
// hints, https://developers.cloudflare.com/cache/about/early-hints/
//
// This will turn into a nice speed-up since the server side render can take some time
// while we fetch all the information from the homeserver and the page can have all of
// the assets loaded and ready to go by that time. This way there is no extra delay
// after the page gets served.
function setHeadersToPreloadAssets(res, pageOptions) {
assert(res);
assert(pageOptions);
assert(pageOptions.entryPoint);
const { styles, fonts, images, preloadScripts } = getDependenciesForEntryPointName(
pageOptions.entryPoint
);
// Work on assembling the `Link` headers
//
// Note: Any docs for the `<link>` element apply to the `Link` header. "The `Link`
// header contains parameters [that] are equivalent to attributes of the `<link>`
// element."
// (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link#parameters)
//
Fix preload link headers (#185) Follow-up to https://github.com/matrix-org/matrix-public-archive/pull/171 and https://github.com/matrix-org/matrix-public-archive/pull/175 where they broke because we went from scripts to modules. Part of https://github.com/matrix-org/matrix-public-archive/issues/132 Before this PR, we were seeing these warning in the Chrome devtools console: ``` A preload for 'foo' is found, but is not used because the request credentials mode does not match. Consider taking a look at crossorigin attribute. ``` This is caused by a credentials mode mismatch between the `<script type="module">` tag and the `Link` header. A `<script type="module">` with no `crossorigin` attribute indicates a credentials mode of `omit` and a naive `Link: </foo-url>; rel=preload; as=script;` has a default credentials mode of `same-origin`, hence the mismatch and warning we're seeing. We could set the credentials mode to match using `Link: </foo-url>; rel=preload; as=script; omit` but there is an even better option! We can use the dedicated `Link: </foo-url>; rel=modulepreload` link type which not only downloads and puts the the file in the cache like a normal preload but the browser also knows it's a JavaScript module now and can parse/compile it so it's ready to go. --- Future consideration: Adding `nopush` to preload link headers. Many servers initiate an HTTP/2 Server Push when they encounter a preload link in HTTP header form otherwise. Do we want/care about that (or maybe we don't)? (mentioned in https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf#6f54) --- References for preload `Link` headers: - https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf#6f54 - https://html.spec.whatwg.org/multipage/links.html#link-type-preload - https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/#headers - https://developer.chrome.com/blog/modulepreload/#ok-so-why-doesnt-link-relpreload-work-for-modules
2023-04-26 02:29:57 -06:00
// XXX: Should we add `nopush` to the `Link` headers here? Many servers initiate an
// HTTP/2 Server Push when they encounter a preload link in HTTP header form
// otherwise. Do we want/care about that (or maybe we don't)? (mentioned in
// https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf#6f54)
const styleLinks = styles.map((styleUrl) => {
return `<${styleUrl}>; rel=preload; as=style`;
});
// We use `crossorigin` because fonts are fetched with anonymous mode "cors" and
// "same-origin" credentials mode (see
// https://drafts.csswg.org/css-fonts/#font-fetching-requirements). `crossorigin` is
// just short-hand for `crossorigin=anonymous` (see
// https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin and
// https://html.spec.whatwg.org/multipage/infrastructure.html#cors-settings-attribute).
const fontLinks = fonts.map((fontUrl) => {
return `<${fontUrl}>; rel=preload; as=font; crossorigin`;
});
const imageLinks = images.map((imageUrl) => {
return `<${imageUrl}>; rel=preload; as=image`;
});
Fix preload link headers (#185) Follow-up to https://github.com/matrix-org/matrix-public-archive/pull/171 and https://github.com/matrix-org/matrix-public-archive/pull/175 where they broke because we went from scripts to modules. Part of https://github.com/matrix-org/matrix-public-archive/issues/132 Before this PR, we were seeing these warning in the Chrome devtools console: ``` A preload for 'foo' is found, but is not used because the request credentials mode does not match. Consider taking a look at crossorigin attribute. ``` This is caused by a credentials mode mismatch between the `<script type="module">` tag and the `Link` header. A `<script type="module">` with no `crossorigin` attribute indicates a credentials mode of `omit` and a naive `Link: </foo-url>; rel=preload; as=script;` has a default credentials mode of `same-origin`, hence the mismatch and warning we're seeing. We could set the credentials mode to match using `Link: </foo-url>; rel=preload; as=script; omit` but there is an even better option! We can use the dedicated `Link: </foo-url>; rel=modulepreload` link type which not only downloads and puts the the file in the cache like a normal preload but the browser also knows it's a JavaScript module now and can parse/compile it so it's ready to go. --- Future consideration: Adding `nopush` to preload link headers. Many servers initiate an HTTP/2 Server Push when they encounter a preload link in HTTP header form otherwise. Do we want/care about that (or maybe we don't)? (mentioned in https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf#6f54) --- References for preload `Link` headers: - https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf#6f54 - https://html.spec.whatwg.org/multipage/links.html#link-type-preload - https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/#headers - https://developer.chrome.com/blog/modulepreload/#ok-so-why-doesnt-link-relpreload-work-for-modules
2023-04-26 02:29:57 -06:00
// We use `rel=modulepreload` instead of `rel=preload` for the JavaScript modules
// because it's a nice dedicated thing to handle ESM modules that not only downloads
// and puts it in the cache like a normal `rel=preload` but the browser also knows
// it's a JavaScript module now and can parse/compile it so it's ready to go.
//
// Also as a note: `<script type="module">` with no `crossorigin` attribute
// indicates a credentials mode of `omit` so you will run into CORS issues with a
// naive `Link: <thing_to_load.js>; rel=preload; as=script;` because it defaults to
// `same-origin` and there is a mismatch. (see
// https://developer.chrome.com/blog/modulepreload/#ok-so-why-doesnt-link-relpreload-work-for-modules)
// (and spec: https://html.spec.whatwg.org/multipage/links.html#link-type-preload ->
// https://fetch.spec.whatwg.org/#concept-request-credentials-mode). There isn't a way
// to make the link match `omit`. You could update both the link and `<script
// type="module">` to one of the other `crossorigin` values though.
Fix preload link headers (#185) Follow-up to https://github.com/matrix-org/matrix-public-archive/pull/171 and https://github.com/matrix-org/matrix-public-archive/pull/175 where they broke because we went from scripts to modules. Part of https://github.com/matrix-org/matrix-public-archive/issues/132 Before this PR, we were seeing these warning in the Chrome devtools console: ``` A preload for 'foo' is found, but is not used because the request credentials mode does not match. Consider taking a look at crossorigin attribute. ``` This is caused by a credentials mode mismatch between the `<script type="module">` tag and the `Link` header. A `<script type="module">` with no `crossorigin` attribute indicates a credentials mode of `omit` and a naive `Link: </foo-url>; rel=preload; as=script;` has a default credentials mode of `same-origin`, hence the mismatch and warning we're seeing. We could set the credentials mode to match using `Link: </foo-url>; rel=preload; as=script; omit` but there is an even better option! We can use the dedicated `Link: </foo-url>; rel=modulepreload` link type which not only downloads and puts the the file in the cache like a normal preload but the browser also knows it's a JavaScript module now and can parse/compile it so it's ready to go. --- Future consideration: Adding `nopush` to preload link headers. Many servers initiate an HTTP/2 Server Push when they encounter a preload link in HTTP header form otherwise. Do we want/care about that (or maybe we don't)? (mentioned in https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf#6f54) --- References for preload `Link` headers: - https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf#6f54 - https://html.spec.whatwg.org/multipage/links.html#link-type-preload - https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/#headers - https://developer.chrome.com/blog/modulepreload/#ok-so-why-doesnt-link-relpreload-work-for-modules
2023-04-26 02:29:57 -06:00
//
// Spec: https://html.spec.whatwg.org/multipage/links.html#link-type-modulepreload
const scriptLinks = preloadScripts.map((scriptUrl) => {
Fix preload link headers (#185) Follow-up to https://github.com/matrix-org/matrix-public-archive/pull/171 and https://github.com/matrix-org/matrix-public-archive/pull/175 where they broke because we went from scripts to modules. Part of https://github.com/matrix-org/matrix-public-archive/issues/132 Before this PR, we were seeing these warning in the Chrome devtools console: ``` A preload for 'foo' is found, but is not used because the request credentials mode does not match. Consider taking a look at crossorigin attribute. ``` This is caused by a credentials mode mismatch between the `<script type="module">` tag and the `Link` header. A `<script type="module">` with no `crossorigin` attribute indicates a credentials mode of `omit` and a naive `Link: </foo-url>; rel=preload; as=script;` has a default credentials mode of `same-origin`, hence the mismatch and warning we're seeing. We could set the credentials mode to match using `Link: </foo-url>; rel=preload; as=script; omit` but there is an even better option! We can use the dedicated `Link: </foo-url>; rel=modulepreload` link type which not only downloads and puts the the file in the cache like a normal preload but the browser also knows it's a JavaScript module now and can parse/compile it so it's ready to go. --- Future consideration: Adding `nopush` to preload link headers. Many servers initiate an HTTP/2 Server Push when they encounter a preload link in HTTP header form otherwise. Do we want/care about that (or maybe we don't)? (mentioned in https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf#6f54) --- References for preload `Link` headers: - https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf#6f54 - https://html.spec.whatwg.org/multipage/links.html#link-type-preload - https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/#headers - https://developer.chrome.com/blog/modulepreload/#ok-so-why-doesnt-link-relpreload-work-for-modules
2023-04-26 02:29:57 -06:00
return `<${scriptUrl}>; rel=modulepreload`;
});
res.set('Link', [].concat(styleLinks, fontLinks, imageLinks, scriptLinks).join(', '));
}
module.exports = setHeadersToPreloadAssets;