Tooling · for app developers, and whoever answers for a site's privacy notice · Markdown

306. What a page asks other origins for

The question this chapter answers: when a visitor opens one of your pages, which requests leave for hosts other than yours? How do you keep one of them on your own origin when privacy or offline use calls for it?

The defaults

A page FSR renders asks your origin for its document, its modules, its stylesheets, its images and its fonts. Something else is reached only when the application names it. Each one shows in the boot report's policy rows, which the stock host prints under fsr serve --verbose, since the host adds every other origin a page reaches to the Content-Security-Policy it sends.

WhatReachesWhenKeep it on your origin
A script from scriptTag or loadScriptthe script's hoston load, when idle or on the first interaction, as writtenserve the file from a [[static]] root
An <Embed>'s stillthe provider's image CDN, i.ytimg.com or Vimeo'son load[media] embed_stills = "local"
An <Embed>'s playerwww.youtube-nocookie.com or player.vimeo.comonly when the visitor plays itnothing: the video lives there
A <video>, <audio>, <source> or <track> with a URL on another hostthat hostas the element's preload saysimport the file, which serves it from your origin
An image through a named [images] sources entrythe image serviceon loadimport the file instead
A font with [fonts.<key>] remotethe provider's stylesheet and fileson loadfsr fonts add, which downloads the files under fonts/
[images] base and [fonts] basethe CDN namedon loadleave them unset

Nothing in this list happens unless the application wrote it. A card image, a sitemap, a robots file and the client modules are always served from your origin.

Video embeds

<Embed provider="youtube" id="…" title="…" /> renders a still with a play button. The player's iframe loads only when the visitor clicks, which is why a page with three embeds costs three images rather than three players:

tsx
import { Embed } from "@snapfire/fsr-client/react";

<Embed provider="youtube" id="f_WuRfuMXQw" title="Rick Astley - Never Gonna Give You Up" />

By default the still is the provider's own image, so the page asks the provider's image CDN for it on load. YouTube's still is named by the id. Vimeo's comes from Vimeo's public video API, which the build asks once and keeps the answer under generated/embeds/.

To keep the provider out of the page until the visitor plays, fetch the stills at build:

toml
[media]
embed_stills = "local"

The build then fetches each still once into generated/embeds/ and serves it from your origin under its content hash. The page asks the provider for nothing until a click and shows its stills offline, as a service worker caches them with the rest of the site. A fetch that fails, a build with no network for example, falls back to the provider's image and says so in the build output. An id the page computes has no still the build can fetch: a YouTube placeholder shows the still its CDN names by the id. A Vimeo one shows the title alone. fsr build --verbose names each such <Embed> with an embed row. The player always comes from the provider, since that is where the video is.

The player loads from youtube-nocookie.com, YouTube's domain for embeds that sets no cookie until the video plays.

Content-Security-Policy

With a [document.csp] the host widens each directive by the origins above that the build can read: script-src for a script's host, img-src for a still or any image whose URL starts with a written-out origin, frame-src for a player and media-src for a media element's URL. A URL whose origin is computed at request time is one the build cannot read, so its origin goes in the policy by hand; for a script the boot report names the row that computes it. Chapter 305 has the policy for scripts in full.

The lab

The conference app's venue page has two embeds, Rick Astley and the Gummibär song. Open /venue and read the browser's network log: two images from i.ytimg.com and nothing from youtube-nocookie.com. Click the first and its player loads, while the second stays a still. Set [media] embed_stills = "local", build and load the page again: the stills come from /static/js/app/generated/embeds/ and the log shows no request to YouTube until the click.