Nothing to declare¶
Before the move nobody had run the check. The visitor’s browser does what the page tells it to, and a page can tell it to contact several companies the hosting contract never mentions: analytics, web fonts, JavaScript from a CDN, embedded video, maps, contact forms, search, comments, social embeds, cookie and consent tooling, error reporting, uptime monitoring. Each of those is a request the visitor makes, or data the visitor leaves behind, that goes somewhere other than the site. For a stack of static sites this layer may hold almost nothing, like it does for Purple Lantern Ltd., which is the nicest thing that can be said about any layer, and the scrap for it says: look in the output, not in the source, because themes add things the source never mentions.
The check¶
For each site, every hostname in every src, href, url() and fetch in the built HTML, CSS and JavaScript,
listed and counted:
grep -rhoE 'https?://[A-Za-z0-9.-]+' build/html | sort | uniq -c | sort -rn
Links to other sites appear in the list, since a page may point anywhere. What has no business in it is a hostname
inside a src, a url() or a stylesheet href, because those are requests the browser makes without being asked.
The list¶
Seven sites, three kinds, and the list came back short. The Sphinx theme downloads its fonts at build time from Google into a cache that each documentation repository commits, and the built site serves the files itself, so a visitor’s browser fetches them from the site and never from Google. The blog’s templates and assets are its own and reference nothing outside. Zola’s search index and Sphinx’s are built at build time and queried in the browser. No comments, no analytics, no embeds.
One outbound edge, on one page. The contact form on the main site posts to Formward, a form
backend in Stockholm on Swedish datacentres, and the site’s CSP permits no other form action. Formward
runs five layers of spam protection in order, and the first three are on for the
form: an origin allowlist, which rejects any request whose Origin header is not on the list configured for the form
with a 403 before looking at the body; a per-IP and per-form rate limit, which answers 429 once submissions from the
same address pass the configured count in a rolling window, before anything is stored; and the _gotcha honeypot, a
hidden field real visitors never fill in, so that a submission with it filled is stored as spam and answered with
success, giving the bot no signal it was caught. Those three are what the free plan gives, and they have been enough:
no spam has come through. The two layers above them stay off. Turnstile would add a script fetched from Cloudflare on
every page view, which is one more request to a US company, the thing this page exists to count. AI spam scoring is a
Pro feature that sends the submission to an AI provider for a verdict, and a contact form with no spam has nothing to
send. What Formward keeps of the visitor is not the IP address: it is pseudonymised on receipt with a keyed
HMAC-SHA-256 and only that value is written, and the docs say raw addresses are never written to disk or logs.
One item that is not the site’s decision. The server logs are statichost’s, under statichost’s retention.
Declared¶
Static HTML and CSS, local assets, no JavaScript services, no analytics, and one form that posts to a Swedish backend. The website has surprisingly little to betray anyone with.
Granny Weatherwax’s cottage has nothing in it worth stealing, and she keeps it that way on purpose. A house with nothing to take needs no lock and gets no burglars.