Skip to content

Hosting and headers

Two independent things can go wrong when you pick a host for a rendered catalog site, and neither shows up until you've already deployed.

Failure mode 1 — wrong domain shape. No page or fetch this package emits is prefix-aware (see Known limitations): every client-side request is a literal root-relative path like /data/catalog/catalog.json or /p/<ns>/<pkg>.json. Serve the site under any path prefix and every one of those requests resolves against the wrong location — the symptom is a blank site or every asset 404ing.

Failure mode 2 — _headers goes unread. src/sources/mirror.ts emits a Cloudflare Pages/Netlify-format _headers file that sandboxes every mirrored, untrusted /p/* path (a README, a logo, a package root — content handed to this renderer by a configured source, never authored by it). Only Cloudflare Pages and Netlify actually read that file format. On every other host it still ships to dist/_headers but does nothing: the untrusted content is then served with none of the isolation this package assumes is in place.

The two headers, and exactly which paths they cover (renderHeaders() in src/sources/mirror.ts):

/p/*
  Content-Security-Policy: sandbox
  X-Content-Type-Options: nosniff

/index/<label>/p/*
  Content-Security-Policy: sandbox
  X-Content-Type-Options: nosniff

(the leading /p/* block only exists when one of your sources sets root: true; every other source gets its own /index/<label>/p/* block regardless.) Translating those two rules into your host's own per-path response-header mechanism, if it has one, is the deployer's responsibility — this package does not ship or test a translation for any host but Cloudflare Pages and Netlify.

Decision table

Host Reads _headers? Domain root by default? What you must do yourself
Cloudflare Pages Yes, natively — the format this package targets Yes, both the *.pages.dev subdomain and a configured custom domain Nothing, for these two concerns
Netlify Yes — the format Cloudflare Pages adopted from Netlify Yes, both the *.netlify.app subdomain and a configured custom domain Nothing, for these two concerns
GitHub Pages No — the file ships but is inert Only a user/org site (https://<user>.github.io/); a project site (https://<user>.github.io/<repo>/) is a subpath Use a user/org site or a custom domain, not a project site path; translate the two headers into GitHub Pages' own mechanism, or accept the risk knowingly
GitLab Pages No Only with the unique-domain feature, or a configured custom domain — the traditional https://<namespace>.gitlab.io/<project>/ shape is a subpath Enable a unique/custom domain; translate the headers yourself, or accept the risk knowingly
S3 + CDN No Depends entirely on your CDN's routing/domain config Configure the CDN for a root domain; add the two headers via whatever edge-function/response-header mechanism your CDN offers, or accept the risk knowingly
Self-managed nginx/Caddy No Whatever you configure — typically a root domain Configure a root domain; add the two headers as server-level response-header rules, or accept the risk knowingly

"Accept the risk knowingly" is a real option, not a euphemism for skipping the work: it means deploying without the CSP sandbox / MIME-sniffing protection on mirrored third-party content and deciding that's acceptable for your sources (a single, fully-trusted internal index, for instance). It is not the default this package assumes — see Security and trust model for what the sandbox is defending against.