Rebuilding christlieb.eu with Astro: from a Laravel app to a static site
A while ago I revived this blog on a Laravel app with a Filament admin, and not long after wrote about hosting it with Coolify. That setup was fun, but it was also a lot of moving parts for what this site actually is: a handful of static pages and a blog. A database, a queue, Horizon, a server — none of it earns its keep here. So I rebuilt the whole thing as a static Astro site and put it on GitHub Pages.
Here is why, and the parts of the migration that were actually interesting.
Why static
The honest reason is maintenance. A server that runs my personal website is a server I have to keep patched, monitor, and pay for. For a site with no logins, no forms that hit a database, and content that changes a few times a year, that is all cost and no benefit.
A static build flips it around: the output is plain HTML, it’s free to host, it’s fast
because there’s nothing to compute per request, and there is no runtime to attack or break.
The trade-off is that there’s no admin UI anymore — I write Markdown in the repo and git push.
For a developer, that’s not a downside. It’s the nicer workflow.
The content was already half-migrated
The lucky part: my content already lived as flat files. The Filament setup imported pages and
posts from YAML, so the posts were never truly trapped in the database. Moving them into Astro’s
content collections was mostly a mechanical conversion — YAML front matter plus the Markdown
body becomes an .mdx file per post, validated by a small Zod schema. That schema is worth its
weight: a typo in a date or a missing excerpt fails the build instead of shipping broken.
The Laravel-isms that needed replacing
A few things were doing real work in the old stack and needed a static equivalent:
- Syntax highlighting. I was using Torchlight, which sends code to an API at build time. Astro ships Shiki built in, so highlighting now happens locally with no external call and no token to manage. I kept the copy-to-clipboard button as a tiny client script.
- The CV PDF. The old site generated it on demand with DomPDF. On a static site there’s no
“on demand”, so I render a chrome-less
/cv/printpage and let Playwright print it to a PDF at build time. Same source of truth (one YAML file) drives both the web CV and the PDF. - Email obfuscation. The contact and legal pages used a Blade trick to assemble my address
from
data-attributes so scrapers don’t get a cleanmailto:. That became a small Astro component doing the same thing. - RSS, sitemap, and structured data all carried over — Astro has first-party integrations for the first two, and the JSON-LD is just a script tag in the layout.
The bleeding-edge tax
I built this on Astro 7 (beta) with Vite 8, which was mostly smooth — but not free. The one
thing that bit me was astro-expressive-code: at the time it didn’t declare support for Astro 7,
so it wouldn’t install. Rather than force it, I dropped it and used the built-in Shiki highlighter
plus a few lines of CSS for the copy button. Lesson as old as time: on a beta toolchain, every
integration is a dependency you’re betting on, so keep the bets small.
The deploy gotcha that cost me an hour
GitHub Pages with a custom domain has one ordering trap. I set the custom domain before DNS was
pointing at GitHub. GitHub tried to verify the domain, failed (DNS still pointed elsewhere), and
quietly never queued a TLS certificate. The site served fine over HTTP but presented the wrong
*.github.io certificate over HTTPS.
The fix was simply to point DNS at GitHub’s A records first, then re-add the custom domain so
verification passes and Let’s Encrypt issues the cert. Once it shows up as approved, enable “Enforce
HTTPS” and you get the HTTP→HTTPS redirect for free. Order matters: DNS first, domain second.
What I gave up
To be fair about the trade-offs:
- No admin UI. Editing is Markdown in git. I prefer it, but it’s a real change.
- GitHub Pages can’t set custom HTTP headers, so there’s no CSP or HSTS layer. For a static
personal site I’m comfortable with that; if I needed headers I’d move to Cloudflare Pages or Netlify,
which serve the same
dist/with a config file for headers.
Was it worth it
Yes. The site is faster, it costs nothing to run, there’s nothing to keep patched, and writing a post is now “add a Markdown file and push.” That last part matters most — the lower the friction to publish, the more likely I am to actually do it.
Next on the list: figuring out scheduled publishing, so I can write posts ahead of time and have them go live on their own. More on that soon.