Moving static pages off my home server to Cloudflare Pages
A few compliance pages had no business being served from my living room. So they aren't anymore.
For a while my home server was doing something faintly embarrassing. A reverse proxy on it — Caddy, terminating TLS on a wildcard — was answering for a clutch of small static pages: a privacy notice, a terms page, the boilerplate an SMS provider insists you publish before they will let an A2P campaign go live. Pure HTML and a stylesheet. No database, no sessions, nothing that changed from one month to the next. They reached the internet through a single port-forward on the home router, which meant every request for a paragraph of legal text resolved, eventually, to my residential IP.
That arrangement worked, which is the dangerous kind of working. It survived because nothing tested it. But the failure modes were all stacked behind one box and one consumer uplink: if the server was down for a kernel update, the pages were down. If the ISP renumbered me — and they do, without warning — the DNS lagged and the pages were down. And a vendor's compliance crawler, the one entity guaranteed to check the pages at the worst possible moment, would record the outage and hold it against me. For content that genuinely never changes, that is a lot of ways to fail.
The other thing nagging at me was the IP itself. Publishing static text does not require advertising where I live, and the port-forward did exactly that. Anyone who pulled the page, resolved the host, and noted the address had my home network's front door. I wanted the home IP out of the path entirely, not obscured — gone.
Why Pages, specifically
The requirements were small enough to be honest about. Serve a handful of files over HTTPS. Stay up when my house does not. Cost nothing, or close to it, because this is a few kilobytes of obligation, not a product. Keep the apex domain's mail flowing, untouched, because the one thing worse than a compliance page being down is the family email going down alongside it.
Cloudflare Pages fit without negotiation. It is static hosting on their edge, free for this scale, with TLS and a global cache I do not have to think about. The deploy is a single command, which matters less for the first push and a great deal for the fourteenth, when I have forgotten every flag I used. I keep the source in a directory and ship it with Wrangler:
# From the project root — the dist/ dir holds the built static files.
wrangler pages deploy ./dist --project-name subnetted-sms
That uploads the directory, diffs it against the last deployment so unchanged files are skipped, and hands back a versioned *.pages.dev URL within a few seconds. Every push is its own immutable build with its own preview URL, which turns "did that change actually land?" from a question into a link I can open. For something I touch twice a year, the value is precisely that I do not have to remember how it works between visits.
Clean URLs, for free
One detail I had braced to fight turned out to need no fighting. Pages serves clean, extensionless paths out of the box: a file named about.html in the project answers at /about, and index.html in a directory answers at that directory's root. On the home box I had been doing this by hand — a try_files-style dance in the Caddyfile to strip the suffix and avoid a redirect loop. Here it is just the default behaviour of the static server. The links in the markup stay clean, the URLs stay clean, and there is no rewrite rule of mine to rot.
The DNS, kept at the registrar
I did not want to move the zone. The apex handles email through a provider I am in no hurry to re-point, and nameserver migrations are the kind of change that looks trivial until an MX record goes missing for an afternoon. So I attached only the www subdomain to the Pages project as a custom domain, using an external CNAME. DNS stays exactly where it has always lived, at the registrar; Cloudflare is not in the resolution path for anything but that one hostname.
The change at the registrar was small and deliberate:
- Delete the existing
wwwA record — the one pointing at the home WAN IP. - Create a
wwwCNAME pointing at the project's<project>.pages.devhostname. - Leave the apex, the
MXrecords, and SPF/DKIM/DMARC untouched.
Cloudflare validates the custom domain by watching for exactly that CNAME, then provisions a certificate covering www and begins serving. The apex still forwards to www the way it did before, so old links do not break. Mail kept flowing throughout; I checked, because not checking is how you learn.
The tradeoff worth naming
There is one thing you give up, and it is worth saying plainly rather than discovering it later. On Pages you do not get to pin the certificate issuer to a particular CA. Cloudflare chooses — and may change — which authority signs the leaf. If you have a policy that says "this hostname must always chain to issuer X," Pages will not honour it, and no amount of configuration will change that.
The certificate is valid, publicly trusted, and renews itself without my involvement. For static text that asks nothing of the visitor, that is the entire requirement — and "renews itself" beats any issuer I could have named.
a note I left for the version of me who reads this in a year
That is the trade in full. I surrendered a lever I was never pulling — issuer pinning — in exchange for a certificate lifecycle I no longer touch. For pages whose only job is to exist, be reachable, and present a green padlock to a compliance crawler, it is not a close call.
What actually changed
The home server lost a job it should never have held. The port-forward is closed, one fewer thing listening on the residential uplink and one fewer reason for my IP to appear in a vendor's logs. The pages now live on infrastructure that stays up when I am patching kernels or the ISP is renumbering me, and they answer from whichever edge node is nearest the reader rather than from a box in my house.
None of this is clever. It is the unglamorous correct move: take the stateless thing off the stateful, lovingly hand-tended machine, and put it somewhere boring and replicated that asks nothing of you. The home server is for the things that genuinely need to be home. A page of legal boilerplate was never one of them, and now it knows its place.