Engineering 4 min read

How this site is built

A short colophon: why jensskott.com is static Astro on Cloudflare, why I own the canonical, and how the OG images and analytics are wired.

I’ll write a lot here about apps, bugs, and the unglamorous parts of shipping software. It felt only fair to start by explaining how the place I’m writing it is put together — partly as a colophon, partly because the choices here are the same ones I argue for everywhere else.

Static Astro, no server

This site is Astro building to static HTML, served from Cloudflare Pages. There’s no SSR adapter, no running server, no database. Every page is a file on a CDN edge node by the time you read it.

That’s not minimalism for its own sake. It’s the same rule I apply to product backends: reach for the least backend the data model requires. A personal site has no shared state, no per-user data, nothing server-authoritative. So it gets no server. The payoff is real — pages load instantly, there’s nothing to patch at 2am, and the hosting bill is zero.

I own the canonical

The most important decision isn’t visible on the page. It’s that this domain is the canonical home for everything I write.

When you publish first on Medium or Substack, those platforms keep the SEO credit — your own site, if you mirror the post, becomes the duplicate. I want the opposite. I write here, then syndicate out to Medium and Dev.to with a rel=canonical tag pointing back. Search engines consolidate the ranking signal to jensskott.com, even when a reader lands on the copy somewhere else.

Own the canonical, rent the reach. Every post compounds this domain’s authority instead of someone else’s.

Social cards, generated at build

Every post gets an Open Graph image — the card that shows up when a link is shared on X, LinkedIn, or in a chat. I didn’t want to design one by hand for every post, and I didn’t want to depend on an external screenshot service.

So they’re generated at build time with astro-og-canvas: each post’s title and description are rendered onto a branded card and written out to /og/blog/<slug>.png. No binaries committed, no runtime calls. If a post sets its own hero image, that wins; otherwise the generated card is the fallback. The meta tags are wired in a single SEO.astro component so no page can forget them.

Analytics from day one

GA4 and Google Search Console are connected from the first deploy, not bolted on after I “have an audience.” You can’t analyse traffic you didn’t measure, and the most interesting data is always the early data you forgot to capture.

The measurement ID is public by design — it ships in the HTML — and it’s read from an environment variable so the same build works locally and in production. Until the property exists, the tag simply doesn’t render, so the site never ships a broken analytics call.

The workflow is deliberately boring

There’s no CI pipeline. I test locally, push to main, and Cloudflare Pages builds and deploys. A pre-commit hook runs lint, format, type-check, and the relevant tests; a pre-push hook runs the full suite before anything reaches main. That’s the entire release process.

For a solo site, a heavyweight CI/CD setup is just more surface area to maintain. The git hooks give me the safety net where it matters — before code leaves my machine — without a pipeline to configure or debug.


None of this is novel. It’s just consistent: least infrastructure that does the job, own the thing that compounds, measure from the start, and keep the machinery boring so the energy goes into the writing and the apps. That’s the through-line for most of what I’ll post here.

FAQ

Why not Medium or Substack?

Those are great for reach but they keep the SEO equity. The canonical version lives here and gets syndicated out with canonical tags pointing back, so search engines credit this domain even when you read the copy elsewhere.

Why static instead of a server?

Nothing on this site needs a server. Static output on Cloudflare's edge is faster, cheaper, and has nothing to keep running. I add a backend only when shared, server-authoritative state actually demands one.

#astro #cloudflare #seo #building-in-public