Rebuilding my 2016 Drupal site with Astro 7: field notes
For over a decade, my personal site ran on Drupal — because of course it did. I build Drupal platforms for a living. But the site had become the cobbler’s barefoot child: a single-page CV on an ageing Drupal 9, with 58 render-blocking stylesheets, no structured data, no sitemap, and content frozen somewhere around 2019.
I rebuilt it with Astro 7 — and this post documents the SEO-first architecture, because it is the same recipe I now recommend for any content site that does not need a CMS-driven front end.
Why static, and why Astro
My old Drupal site served exactly one page to anonymous visitors. Running a full CMS stack — PHP, database, security updates, cache layers — to serve what is effectively a static document is engineering debt with no return.
Astro’s value proposition fits this case perfectly:
- Zero JavaScript by default. Every byte of JS must justify itself. My content pages ship none.
- Content collections with typed frontmatter — the blog is markdown files in git, validated at build time.
- First-class image and font tooling — more on that below.
- The output is plain HTML on a CDN. There is nothing to hack and nothing to patch on a Tuesday night.
Note for my Drupal colleagues: this is not “Drupal is dead” talk. Editorial platforms with dozens of contributors need a real back office — that is what Drupal is for. Your personal site does not.
The parts that matter for SEO
Fonts without layout shift
Astro’s built-in Fonts API downloads the fonts at build time, self-hosts them with unicode-range subsets, and —
the killer feature — generates size-adjusted fallback fonts automatically. The fallback Arial is metrically
compatible with the real font, so the swap causes near-zero cumulative layout shift. This used to require a manual
pipeline; it is now one config block.
One JSON-LD graph per page
Instead of scattering disconnected schema snippets, every page emits a single @graph linking entities by @id:
WebSite → Person (my entity, with sameAs links to drupal.org, GitHub and LinkedIn) → the page node
(WebPage, ProfilePage, BlogPosting…) → BreadcrumbList. Search engines — and increasingly LLMs — get a
coherent identity graph instead of confetti.
For a personal brand, the Person entity is the whole game: it is what lets Google (and AI assistants) connect your
site, your open-source work and your profiles into one “who is this” answer.
hreflang only where translations exist
The site is bilingual (English first, French under /fr/). A classic multilingual mistake is generating hreflang
pairs for every URL by prefix substitution — including pages that were never translated. My hreflang links are
generated from an explicit route map, so a page only claims an alternate that actually exists. Partial translation
is fine; lying to crawlers is not.
Open Graph images from the build
Every page and post gets a branded 1200×630 OG image, rendered at build time with satori (HTML-ish template → SVG) and resvg (SVG → PNG). No runtime, no third-party service — just static PNGs on the CDN, in the brand’s navy and azure.
The boring plumbing, done properly
robots.txtgenerated from config, pointing at the sitemap index.- Sitemap with real
lastmodvalues. - 301 redirects for every URL of the old Drupal site (
/node/1, I will not miss you). - Canonicals, OG/Twitter tags and meta descriptions on every page — enforced by a single
<Seo>component, so a page cannot be created without them.
Deployment: Cloudflare
The site deploys as static assets on Cloudflare Workers, with one server-rendered route: the contact form endpoint (Turnstile for bots, Resend for delivery — about fifty lines of code). Since Cloudflare acquired the Astro team in January 2026, this is the most invested-in deployment path in the ecosystem, and the free tier covers a personal site entirely.
The result
Lighthouse does not go higher than this, and the page weight went from megabytes of CSS/JS soup to a few dozen kilobytes. But the honest metric is different: the old site had one indexable page; this one has a content architecture designed to grow — services, case studies, and this blog.
The next posts will go deeper into individual pieces: the JSON-LD graph design, Drupal-to-static migration patterns, and Core Web Vitals debugging on Drupal platforms. À suivre.