how-to: hosting the dx-maps folder — caching, compression and CORS
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions

From exp-shepherd's web-boundaries report, checked against main (afb34488): no file in dx-maps/ is
content-hashed today, so everything takes public, max-age=86400, stale-while-revalidate=604800
with an ETag, and immutable only once names are hashed. The boundary file is 1,866 KB raw / 597 KB
gzipped (measured); the smaller -web copies are not on main yet, so not cited. Script embeds need
ACAO * because every embed.script carries integrity + crossorigin=anonymous. The first-paint
figures are rega's cold loads of the UPG-100 list (9.5-16.5 s) and studio's bundle/engine split.
This commit is contained in:
Doxa Maps 2026-09-26 12:18:22 +00:00
parent fa7aee343b
commit ded4b3c105

View file

@ -234,6 +234,46 @@ snippets (leave it empty for relative URLs that work wherever the folder is drop
---
## Hosting the dx-maps folder: caching, compression, CORS
**Why it matters.** On a cold load a map's layers draw about 10–17 s after the page opens (measured
on the UPG-100 list page). The bundle and the map engine take under 2 s of that; the rest is basemap
tiles and data files such as the country-boundary file (1,866 KB raw, 597 KB gzipped). Compression
cuts every visit; caching means a returning reader doesn't download the same files again.
**Every file in `dx-maps/` has a fixed name today.** Nothing is content-hashed, so a new release
overwrites files in place: bundles (`<bundle>/<bundle>.js`), their pages (`index.html`) and
`manifest.json` change whenever the build changes them. Images, fonts and data files change rarely,
but under the same names.
| Files | `Cache-Control` | Why |
|---|---|---|
| everything in `dx-maps/`, as it ships today | `public, max-age=86400, stale-while-revalidate=604800`, with an `ETag` | a revisit revalidates cheaply and never re-downloads unchanged bytes, and a release shows up within a day |
| content-hashed names, if you add them (e.g. `world-adm0.3f9a1c2e.geojson`) | `public, max-age=31536000, immutable` | a changed file gets a new name, so a year-long cache is always safe |
**Never send `immutable` for a fixed name**: an update would stay invisible for a year.
**Compress:** serve `br` or `gzip`, and give data files a JSON content type (`application/json`, or
`application/geo+json` for `.geojson`) so CDNs compress them. Many servers send `.geojson` as
`application/octet-stream`, which CDNs don't compress.
**CORS for script embeds:** every `embed.script` snippet loads the bundle with an `integrity` hash
and `crossorigin="anonymous"`. For that to work on another site, the host must send
`Access-Control-Allow-Origin: *`; without it the browser can't check the hash across origins and refuses
the script. iframe embeds need no CORS.
On Cloudflare Pages or Netlify, a `_headers` file at the folder's root sets the CORS and cache headers:
```
/*
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=86400, stale-while-revalidate=604800
```
Check the content type your host gives `.geojson` files (`curl -sI <url>`) before relying on compression.
---
## Compose maps into a SPA (the app shell)
Several maps + one layout = a single-page app, served from the same `public/` tree.