Astro SEO
Astro is one of the strongest frameworks for SEO — pages prerender to static HTML by default and ship JS only where you ask for it, with strong Core Web Vitals. But defaults aren't guarantees, and it doesn't write your meta tags, sitemap, or canonicals for you. Here's how I run my own Astro site, and how to get the architecture right.
Astro is one of the strongest framework choices for SEO because it prerenders to static HTML by default — your content is in the raw HTML on first crawl, with no rendering queue to wait on for that route. Islands hydrate only the components you mark with a client directive, so most of the page ships without its own hydration JS (Astro can still add page scripts and router JS elsewhere) — none of this guarantees crawlability, indexing, or rankings on its own, so verify the deployed routes. Astro sites also post strong Core Web Vitals numbers. The catch: Astro generates clean HTML but doesn't write your meta tags, canonicals, sitemap, or structured data — those are deliberate build steps, and sitemap discovery targets statically generated routes, so runtime-only URLs need explicit handling. Server Islands (which require an adapter) and View Transitions are SEO-safe when you understand what crawlers actually fetch. I run patrickstox.com on Astro, so this is the stack I actually live in.
TL;DR — Astro is a great choice for SEO. By default it builds your pages into plain HTML files ahead of time, so when Google (or any botA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index.) shows up, your content is already there — no waiting for JavaScript to run. It’s also very fast. The one thing to know: Astro gives you clean HTML, but it does not automatically add your page titlesThe title tag is the HTML title element in a page's head that specifies the document's title. It's the primary source for the SERP title link and a confirmed light ranking factor — but since August 2021 Google doesn't always show it verbatim., sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., or canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content.. You set those up yourself (it’s easy).
What “Astro SEO” means
Astro is a web framework for building websites. Its big idea is “less JavaScript.” Where frameworks like React or Next.js often build the page in your browser using JavaScript, Astro builds your pages into plain HTML files at build time and ships almost no JavaScript unless a part of the page actually needs it. Evidence for this claim Astro prerenders pages as static HTML by default and only sends client JavaScript for explicitly hydrated components. Scope: Astro default static output and islands architecture. Confidence: high · Verified: Astro: Why Astro
That single difference is why Astro is so friendly to search engines. When a search engine crawls a page, it wants to read your content. With a JavaScript-heavy site, the content sometimes isn’t in the page yet — the bot has to run the JavaScript first, and that can be delayed. With Astro, the content is already in the HTML the moment the page loads. There’s nothing to wait for.
I run my own site, patrickstox.com, on Astro — so this isn’t theory for me. It’s the stack I actually build on.
Why Astro is good for SEO
- Content is in the HTML right away. No renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. delay, no missing content.
- It’s fast. Astro sites are lightweight and tend to score very well on Google’s page-experience metrics (Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data.).
- Each page is its own real URL. No fancy single-page-app routing that can confuse crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index..
- JavaScript only loads where it’s needed. A photo gallery or a search box can be interactive without slowing down the rest of the page.
What Astro does NOT do for you
This trips people up. “Astro is SEO-optimized” is only half true. Astro gives you a clean foundation, but it does not automatically:
- Write your page titles and meta descriptionsThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through..
- Generate a sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. (you add a free official plugin for that). Evidence for this claim Astro's official sitemap integration generates sitemap files from statically generated routes. Scope: Astro @astrojs/sitemap integration. Confidence: high · Verified: Astro: Sitemap integration
- Add canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. (which tell Google the “official” version of a page).
- Add structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. (the code that powers rich resultsRich results (formerly 'rich snippets') are enhanced search listings — stars, images, prices, breadcrumbs, video thumbnails, and more — that Google and Bing build from structured data. They're a display feature, not a ranking factor, and eligibility never guarantees they'll show.).
- Create a robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere. file.
None of this is hard — it’s just on you to set it up. Think of Astro as a great kitchen: the appliances are excellent, but you still have to cook.
The simple starter checklist
- Add a sitemap with the official
@astrojs/sitemapplugin. - Set a
title,description, andcanonicalURL on every page (usually from one shared layout file). - Use Astro’s built-in
<Image />component for photos — it makes them load fast and prevents the page from jumping around. - Put a
robots.txtfile in yourpublic/folder.
Want the deep version — Server Islands, View Transitions, static vs server renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM., and the exact mistakes to avoid? Switch to the Advanced tab. For the bigger picture of how search engines handle JavaScript, see JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript..
TL;DR — Astro is architecturally well set up for SEO: pages and endpoints are prerendered to static HTML by default, so there’s no render-queue wave to wait on for that content — but that’s a default, not a universal guarantee, and static/server HTML doesn’t by itself prove crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index., indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., rankings, or Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. for your production URLs. Islands architecture hydrates only the components you mark with a
client:*directive — everything else ships HTML without its own hydrationTurning HTML, CSS, and JavaScript into the final visual page and DOM. JS, though page scripts, other islands, and router enhancements can still add JavaScript elsewhere on the page. Astro doesn’t auto-generate meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored., canonicals, sitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., or structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. — wire those up explicitly, ideally validated by Content Collections + Zod. Server Islands (which require an adapter) serve the static shell with fallback content in the initial document and fetch the deferred content independently afterward — verify what a given crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. actually retrieves rather than assuming. View Transitions usehistory.pushStateand are SEO-safe; Google crawls the underlying MPA pages normally. I run patrickstox.com on Astro, and the features below are the ones I actually use — validated against the deployed site, not just local dev.
Why Astro sidesteps the JavaScript rendering problem entirely
The whole reason JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. is hard is the second wave. Google fetches your raw HTML first, then queues the page for renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. in a headless Chromium later — and that queue is the risk. Google’s own docs describe it: “GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. queues all pages with a 200 HTTP status codeAn HTTP status code is the three-digit number a server returns with every response to tell a browser or crawler what happened to its request — success, redirect, client error, or server error. For SEO the code matters as much as the content: it tells Google and Bing whether to index a page, follow a redirect, retry later, or drop the URL from the index. for rendering unless a robots meta tagThe robots meta tag is an HTML element in a page's head — <meta name=\"robots\" content=\"noindex\"> — that tells search engines how to index and serve that page. It's crawl-then-obey: a page blocked in robots.txt is never fetched, so the tag is never seen. tells Google not to index the page. The page may stay on this queue for a few seconds, but it can take longer than that.” For a client-rendered SPA, your content doesn’t exist until that render wave runs.
Astro’s default output mode is static: pages and endpoints are prerendered to a complete HTML file at build time. So for a route running in that default, the raw HTML is the rendered page. Evidence for this claim Astro uses static output and prerenders routes at build time by default. Scope: Astro default output mode; routes can opt out of prerendering. Confidence: high · Verified: Astro: On-demand rendering There’s no second wave to wait on for that route, because there’s nothing left to execute. Googlebot sees the full content on the first fetch. As Joost de Valk (Yoast’s founder) puts it, “From an SEO perspective, static HTML on a CDN is a better starting point than most CMSes will ever give you.”
That’s the default, though — not a universal property of every route. Set
output: 'server' and the default flips to on-demand rendering (more on that below);
even in a static-default project, an adapter lets an individual route opt out with
export const prerender = false. None of this is guaranteed by architecture alone:
static or server HTML, islands, and adapters don’t by themselves guarantee
crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index., indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., rankings, or Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. — those depend on the deployed
route and the specific crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index., so verify them rather than assuming the framework
handles it.
This also helps with the crawlers that can’t render at all. Google notes plainly that “not all bots can run JavaScript” — and that’s the 2026 reality for most AI crawlers and many third-party tools. Astro’s HTML-first output is readable by every one of them where it’s actually prerendered, not just Googlebot. (This is the same point I make in SEO for a headless CMSA headless CMS decouples content storage and editing (the backend) from how that content is rendered and delivered (the frontend), serving content over an API instead of a built-in templated 'head'. Its SEO outcomes come almost entirely from how the separate frontend renders pages.: the rendering mode is the product.)
Islands architecture: JS only where you ask for it
Astro renders your components to HTML and, in its words, ships “just HTML & CSS,
stripping out all client-side JavaScript automatically.” Interactivity is opt-in. You
mark a component with a client:* directive — client:load, client:idle, or
client:visible — and only that island hydrates with JavaScript. Everything else
stays static HTML. Evidence for this claim Astro client directives selectively hydrate interactive islands while other components remain static HTML. Scope: Astro islands and client directives. Confidence: high · Verified: Astro: Islands
For SEO this is close to ideal. The content Googlebot needs to index is plain HTML, and
your interactive widgets don’t drag it down. client:visible is especially useful: a
below-the-fold component doesn’t even begin hydrating until it scrolls into view, so it
never blocks your LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good.. The concept comes from Jason Miller (creator of Preact), who
described “rendering HTML pages on the server, and inject[ing] placeholders or slots
around highly dynamic regions” for selective hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers..
Two nuances worth being precise about, because competitor coverage tends to blur them:
client:onlyis a different animal. Unlikeclient:load/client:idle/client:visible, aclient:onlycomponent skips server rendering entirely — it produces no HTML on the server at all. Anything indexable placed only inside aclient:onlycomponent isn’t in the document Googlebot fetches; it only exists after the browser hydrates it. Don’t put primary content there.- This is selective hydration, not resumability. Astro re-runs each island’s client code from scratch in the browser; it doesn’t resume execution state that was serialized on the server the way a resumability model (Qwik’s, for example) does. The two get conflated in writeups — they’re not the same mechanism.
And a non-hydrated component isn’t the whole JavaScript picture on a page. “Zero JS” describes
one component without a client:* directive — Astro can still ship page-level <script>
tags, the View Transitions router, and other islands elsewhere on the same page. Describe what
ships per component/route, not as a blanket claim about the page.
What Astro does NOT do automatically
Astro generates clean semantic HTMLSemantic HTML is the practice of using elements like <main>, <article>, <section>, <nav>, <header>, and <aside> to describe what content is, not just how it looks. It helps search engines and assistive tech identify a page's main content more reliably, but it isn't a ranking factor on its own. — and nothing else, SEO-wise. There is no metadata, no canonical, no sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., no structured data out of the box. The myth that “Astro is automatically SEO-optimized” is exactly that. You own:
- Meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored. (title, description, Open GraphOpen Graph (OG) tags are `<meta>` elements in a page's head, defined by the Open Graph protocol (ogp.me, created by Facebook), that describe a page as a shareable object — its title, description, image, URL, and type. They control how a link preview card looks when the page is shared on Facebook, LinkedIn, Slack, Discord, WhatsApp, and iMessage. They are not a direct Google ranking factor, though Google reads og:title, og:image, and og:site_name as inputs to how a result appears., Twitter)
- Canonical URLs
- The sitemap (via the official integration)
- Structured data / JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal.
- robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere.
Astro is the best foundation I’ve used, but it’s a foundation, not a finished house.
The sitemap: @astrojs/sitemap
Install it with npx astro add sitemap. It crawls your statically-generated routes and
emits a sitemap-index.xml plus chunked sitemap-0.xml files at build time. Two things
bite people:
- You must set
site:inastro.config.mjs. Without it, the integration silently generates nothing. This is the single most common “where’s my sitemap?” cause. - You must add the sitemap line to
robots.txtyourself. Astro doesn’t do it.
For control, filter() excludes routes (preview/draft pages — exactly what I use it for
on this site), serialize() lets you set lastmod/changefreq/priority, and the
i18n option emits hreflangHreflang is an annotation (in HTML, HTTP headers, or XML sitemaps) that tells search engines which language and optional region a page targets, and which alternate versions exist. It only works when every page in the cluster references all the others. entries in the sitemap. This is what the current
@astrojs/sitemap docs specify — confirm against your installed version if you’re on an
older release, integration behavior has changed across major versions before.
The scope that trips people up: the integration’s discovery targets statically
generated routes. If any of your URLs only exist at runtime — server-rendered (output: 'server') routes, or routes generated on demand rather than at build time — don’t
assume they’re in the sitemap. Add them explicitly with customPages, then actually
open sitemap-index.xml after a build to confirm they’re there. Don’t take “the
integration handles it” on faith for anything that isn’t a build-time static route.
Meta tags and canonicals: the BaseLayout pattern
Astro has no special <Head> component — you control <head> directly in .astro
files. The standard pattern (and what I do) is a single BaseLayout.astro that takes
title, description, and canonicalURL as props and writes the head. Set the canonical
explicitly on every page and keep it consistent with og:url. You don’t need a library,
but the community astro-seo package (npm) is a convenient one-component wrapper for
title/description/OG/Twitter/canonical if you want it.
Content Collections as an SEO safety net
This is Astro’s underrated SEO feature. Content Collections are a type-safe content
layer for your Markdown/MDX/JSON, with Zod schema validation. That means you can make
title and description required fields — and if a page is missing one, the build
fails. You can’t accidentally ship a page with no title. Query functions
(getCollection(), getEntry()) generate static pages at build time, so the output is
plain HTML when deployed. And because MDX keeps raw markdown as the source of truth, those
files are also clean source content for AI crawlersAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls. and llms.txtllms.txt is a proposed (not adopted) Markdown file at /llms.txt that gives AI systems a curated map of a site's most important pages. Proposed by Jeremy Howard in 2024, it's read mostly by coding agents like Claude Code — not search crawlers — and Google ignores it.-style patterns. This very
site is built on Content Collections with Zod-validated frontmatter.
astro:assets: images done right (with one trap)
The <Image /> component automatically converts to WebP, infers dimensions to “avoid
Cumulative Layout Shift (CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good.),” sets loading="lazy" by default, and requires alt
— a missing alt is a compile error. <Picture /> extends this with AVIF/WebP/fallback
<source> elements.
The trap: the automatic loading="lazy" is wrong for your LCP image (usually the
hero). Lazy-loading your most important image delays it. For above-the-fold images,
override with loading="eager" and fetchpriority="high". Remote images need explicit
width and height.
Server Islands: what crawlers actually see
Server Islands (Astro 4.12+) are the feature most competitor guides get wrong. With
server:defer, a component renders on the server independently of the main page. The
static shell is served immediately; per Astro’s docs, “Your page will be rendered
immediately with any specified fallback content as a placeholder. Then, the component’s
own contents are fetched on the client and displayed when available.”
Two things to be exact about. First, Server Islands require an adapter — they’re an on-demand feature, not something a purely static build produces on its own. Second, the sequence is: the initial document ships with whatever you configured as fallback content, and the island’s real content is a separate, independent request fetched after the page loads, over its own endpoint. That’s a hard floor on what the first document contains — I wouldn’t generalize past that about what every specific crawler does next without testing your own deployed URLs directly.
The SEO consequence is concrete: the static HTML a crawler reads on that first fetch contains your fallback content, not the deferred island content. That’s perfect for what Server Islands are for — personalized, session-specific stuff (login state, cart count, recommendations) that shouldn’t be cached or indexed anyway. It is wrong for primary indexable content. Put anything that needs to rank in the main Astro template, and let Server Islands handle the dynamic bits around it.
Output modes: static vs server, and the per-route override
Astro’s default output mode is static — pages and endpoints prerender to HTML at build
time. Set output: 'server' in astro.config.mjs and the default flips to on-demand
rendering: pages render per request (with an adapter), useful for auth, real-time data, or
personalization beyond what Server Islands cover. Either way, you can override the default
per route: in a static-default project, export const prerender = false opts a route into
on-demand rendering; in a server-default project, export const prerender = true opts a
route back into build-time prerendering. So “my site is static” or “my site is SSR” is
rarely true for every route — check the per-route setting, not just the top-level config.
From a pure SEO standpoint, prerendered and on-demand HTML are equivalent — both deliver complete HTML to a crawler’s first request, once you’ve actually confirmed the route returns a 200 with full markup. On-demand routes can stream their HTML, and slow data or network conditions can delay later chunks, so a streamed response isn’t automatic proof that every piece of content arrived — check it, don’t assume it. The real difference between the two modes is operational: prerendered content is fixed until the next build (or a separately configured runtime refresh) and serves straight from the CDN edge; on-demand content is always current but depends on your adapter and runtime being healthy in production. Don’t treat this as an SEO decision — choose on data freshness and ops, then validate the deployed routes, statuses, redirectsA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't., and response headers rather than extrapolating from what worked in local dev.
View Transitions: SEO-safe, despite the SPA feel
Astro’s <ClientRouter /> (formerly <ViewTransitions />) gives SPA-like soft navigation
using the browser’s View Transitions API and the History API. The key fact: it navigates
with history.pushState, which is exactly what Google recommends for client-side
navigation — Google warns that fragment-based (#hash) URLs are something it “can’t
reliably resolve.” Crucially, View Transitions are a browser-side enhancement. When
Googlebot crawls, it requests each URL and gets a normal, complete HTML page — the MPA
underneath is untouched. The transitions only affect what a human sees in the browser.
So no, View Transitions do not turn your Astro site into an SPA, and they don’t break SEO. A worth-noting gap: Astro’s View Transitions docs have no SEO section, which is probably why the “it breaks SEO” myth persists. To verify on your own site, fetch a few URLs directly and confirm each returns full HTML — don’t take it on faith, check your own deployment.
Common Astro SEO mistakes
- Assuming Astro handles SEO for you. It handles HTML. Meta, canonicals, sitemap, schema are yours.
- Forgetting
site:in the config — your sitemap silently doesn’t generate. - Not adding the sitemap to robots.txt — Astro won’t do it.
- Lazy-loading the LCP image — override the hero with
eager+fetchpriority. - Putting indexable content in a Server Island — crawlers see the fallback, not the content, and Server Islands require an adapter in the first place.
- Chasing a perfect LighthouseLighthouse is Google's free, open-source tool that audits a page under simulated lab conditions and scores it 0–100 across Performance, Accessibility, Best Practices, and SEO. It's lab data — useful for debugging, not a ranking signal. score and stopping there. Speed is a ranking signal, not the ranking signal. A fast empty page doesn’t rank — content, links, and E-E-A-T still do the heavy lifting.
- Putting indexable content only inside a
client:onlycomponent. Unlike the otherclient:*directives,client:onlyskips server rendering entirely — there’s no HTML for that component until the browser hydrates it. - Treating “Astro is static/fast” as an outcome guarantee. Static or on-demand HTML, islands, and adapters are mechanisms — they don’t by themselves guarantee crawlability, indexing, rankings, or Core Web VitalsWeb Vitals is Google's initiative (launched May 2020) for unified page-experience quality signals. Core Web Vitals — LCP, INP, and CLS — are the subset used in ranking; the rest (TTFB, FCP, TBT, Speed Index) are diagnostic, not ranking factors.. Validate the deployed route, not the architecture diagram.
Where this fits in the cluster
Astro is one specific, unusually SEO-friendly answer to the questions JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. raises about rendering, and a popular frontend for headless CMSA headless CMS decouples content storage and editing (the backend) from how that content is rendered and delivered (the frontend), serving content over an API instead of a built-in templated 'head'. Its SEO outcomes come almost entirely from how the separate frontend renders pages. setups. The performance side connects directly to Core Web Vitals in the web-performance cluster, and the “is my content actually in the HTML?” testing discipline is the same one from the crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor. and indexing clusters.
Astro SEO anti-patterns
Concrete mistakes I actually see on Astro sites — not hypothetical ones. Each one is prevention, not diagnosis: catch it before it ships.
Treating Astro as “SEO-optimized” out of the box
Astro gives you fast, clean static HTML, and that’s genuinely a big head start — but it
is not the same as meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored., canonicals, a sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., or structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding.. Why it’s
wrong: teams ship pages with no <title> variation, no canonical, and no sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing.
because “Astro handles SEO,” then wonder why nothing’s indexedStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. as expected. What to do
instead: wire up BaseLayout.astro props for title/description/canonical on day one,
add @astrojs/sitemap, and treat those as required build steps, not defaults.
Forgetting site: in astro.config.mjs
This is the single most common “why is my sitemap empty” report. Why it’s wrong:
@astrojs/sitemap needs an absolute site URL to build absolute <loc> entries — without
site: set, the integration silently produces nothing (no error, no warning). What to
do instead: set site: in astro.config.mjs before you install the sitemap
integration, and check that sitemap-index.xml actually has URLs in it after your next
build.
Leaving the sitemap out of robots.txt
Installing @astrojs/sitemap doesn’t add a Sitemap: line to robots.txt — that’s a
separate manual step people assume is automatic. Why it’s wrong: search engines can
still find the sitemap if you submit it in Search ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results., but you lose the passive
discovery path other botsA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. (and Bing/IndexNowIndexNow is an open push protocol that lets you instantly tell participating search engines (Bing, Yandex, Naver, Seznam, and Yep) which URLs you've added, changed, or removed via a simple HTTP request — and one submission is shared across all of them. Google does not use it.-adjacent crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index.) rely on. What to do
instead: add Sitemap: https://yoursite.com/sitemap-index.xml to your robots.txt in
public/ and confirm it resolves after deploy.
Leaving the default loading="lazy" on the hero image
Astro’s <Image /> component lazy-loads by default, which is the right call for
below-the-fold images and the wrong call for the one image that usually is your LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good.
element. Why it’s wrong: lazy-loading the hero delays the browser from even starting
that image request, which directly hurts your Largest Contentful Paint score. What to
do instead: override the hero/above-the-fold image explicitly with loading="eager"
and fetchpriority="high", and leave every other image on the lazy default.
Putting indexable content inside a Server Island
server:defer is built for personalized, session-specific content — cart counts, login
state, recommendations — not for anything you want ranked. Why it’s wrong: the static
HTML a crawler reads contains the fallback content specified for the island, not what
gets fetched client-side after the page loads, so any primary content placed there is
invisible to search engines on first crawl. What to do instead: keep ranking content
in the main Astro template and reserve Server Islands strictly for the dynamic,
personalized bits that shouldn’t be indexed anyway.
Assuming a fast Lighthouse score is the finish line
Astro sites post strong Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. almost by default, and it’s tempting to stop there. Why it’s wrong: speed is one ranking input among many — a fast, empty, or thin page still doesn’t outrank a slower page with better content, links, and topical depth. What to do instead: treat performance as table stakes you get for free with Astro, then spend the actual optimization effort on content quality, internal linkingLinks between pages on the same site., and the structured-data/meta work Astro doesn’t do for you.
Assuming “static by default” is true for every route
Astro’s static output mode is the default, but it’s a default, not a universal property —
output: 'server' flips it, and prerender can be set per route in either direction.
Why it’s wrong: teams describe their whole site as “static” or “SSR” from the
top-level config and never check individual routes, then get surprised when one route
behaves differently in production. What to do instead: check the per-route
prerender setting for anything you’re reasoning about, and validate the actual response
(status code, full markup, redirectA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't. behavior) on the deployed URL rather than the config
file.
Putting content only inside a client:only component
client:only is not the same as client:load/client:idle/client:visible — it skips
server renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. entirely. Why it’s wrong: a component using client:only produces no
HTML on the server, so anything indexable placed only there is invisible to a crawler
reading the raw response, and it’s easy to reach for client:only for “simplicity”
without realizing the SEO cost. What to do instead: render primary content in a
server-rendered component or the page template; reserve client:only for
interaction-only widgets with nothing indexable inside them.
AI summary
A condensed take on the Advanced version:
- Astro’s default output mode prerendersThe Speculation Rules API is a Chromium browser API (Chrome/Edge 109+) that lets a site tell the browser which same-site pages to prefetch (download the HTML document) or prerender (fully load and render in an invisible tab) before a visitor clicks — so the next navigation can be near-instant. It's a browser-side performance feature for real users, not a crawling, indexing, or ranking signal. to static HTML at build time — for a route in
that default, the raw HTML is the finished page, so Google’s render-queue (the “second
wave”) problem doesn’t apply to it. This is a default, not a universal property:
output: 'server'flips it, andprerendercan override per route in either direction. - Islands architecture hydrates only components marked with
client:*; a component without one ships HTML without its own hydrationTurning HTML, CSS, and JavaScript into the final visual page and DOM. JS (though Astro can still add page scripts, other islands, and router JS elsewhere).client:onlyis the exception — it skips server renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. entirely, so indexable content shouldn’t live only there. This is selective hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers., not resumability.client:visiblekeeps below-the-fold JS from blocking LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good.. - Astro auto-generates nothing SEO-wise — meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored., canonicals, sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., structured data, and robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere. are all deliberate build steps. “Astro is auto-SEO-optimized” is a myth.
@astrojs/sitemapdiscovers statically generated routes — but you must setsite:in the config (or it silently does nothing), add the sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. line to robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere. yourself, and explicitly add any runtime-only URLs viacustomPages.- Content Collections + Zod can make
title/descriptionrequired, failing the build if a page is missing them — an SEO safety net. astro:assets<Image>converts to WebP, sets dimensions (prevents CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good.), lazy-loads by default, and requiresalt. Override the LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good. image withloading="eager"+fetchpriority="high".- Server Islands (
server:defer) require an adapter, serve the static shell + fallback instantly in the initial document, then fetch the island independently afterward. CrawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. reading that first document see the fallback, not the island — never put indexable content there. - Static and on-demand (server) output are SEO-equivalent once verified — both deliver full HTML to crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. on first request, but on-demand routes can stream, so confirm the response actually arrives complete; choose the mode on data freshness and ops, not SEO.
- View Transitions (
<ClientRouter />) usehistory.pushStateand are SEO-safe; Google crawls the underlying MPA pages normally. They’re a browser-side enhancement, not an SPA conversion. - None of the above is an outcome guarantee — static/server HTML, islands, and adapters are mechanisms, not proof of crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index., indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., rankings, or Core Web Vitals. Validate the deployed route.
- Astro posts strong Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. numbers in its own 2023 benchmark — over 50% of Astro sites passed Google’s CWV assessment, well above the industry baseline at the time; treat it as a dated data point, not a live guarantee.
Official documentation
Primary-source documentation from Astro and the search engines.
Astro
- Islands Architecture — how Astro strips client-side JS and hydrates only interactive components.
- Template Directives Reference —
client:load/idle/visible/onlyandserver:defer, including whatclient:onlyskips. - Image Optimization (astro:assets) — the
<Image>/<Picture>components, WebP, dimensions, and CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good.. - @astrojs/sitemap — automatic sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. generation,
filter,serialize, andi18n. - Server Islands —
server:defer, the adapter requirement, fallback content, and deferred client fetching. - On-Demand Rendering — the
static/serveroutput modes, per-routeprerender, and HTML streaming. - Routing Reference — how pages and endpoints prerenderThe Speculation Rules API is a Chromium browser API (Chrome/Edge 109+) that lets a site tell the browser which same-site pages to prefetch (download the HTML document) or prerender (fully load and render in an invisible tab) before a visitor clicks — so the next navigation can be near-instant. It's a browser-side performance feature for real users, not a crawling, indexing, or ranking signal. by default.
- Astro Runtime API Reference —
Response/redirectA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't. handling and default status codes. - Content Collections — type-safe content with Zod schema validation.
- View Transitions —
<ClientRouter />and the History API navigation.
- Understand JavaScript SEO Basics — the render queue, crawlable links, the History API, and the “not all botsA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. run JavaScript” caveat.
- In-Depth Guide to How Google Search Works — crawl → render → indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., and where SSG removes the render step.
Bing / Microsoft
- IndexNow / indexnow.org — the push protocol that pairs well with a static Astro deploy (wire it to your publish step so Bing and Yandex hear about new pages immediately).
Quotes from the source
On-the-record statements from Astro’s docs, Google, and named practitioners. Each search-engine and docs link is a deep link that jumps to the quoted passage on the source page.
Google — the render queue Astro sidesteps
- “GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. queues all pages with a 200 HTTP status codeAn HTTP status code is the three-digit number a server returns with every response to tell a browser or crawler what happened to its request — success, redirect, client error, or server error. For SEO the code matters as much as the content: it tells Google and Bing whether to index a page, follow a redirect, retry later, or drop the URL from the index. for renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. unless a robots meta tagThe robots meta tag is an HTML element in a page's head — <meta name=\"robots\" content=\"noindex\"> — that tells search engines how to index and serve that page. It's crawl-then-obey: a page blocked in robots.txt is never fetched, so the tag is never seen. tells Google not to indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. the page. The page may stay on this queue for a few seconds, but it can take longer than that.” Jump to quote
- “not all botsA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. can run JavaScript” — why HTML-first output helps beyond GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer.. Jump to quote
Astro docs — islands, images, sitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., Server Islands
- “just HTML & CSS, stripping out all client-side JavaScript automatically.” — on islands architecture. Jump to quote
- “infers image dimensions to avoid Cumulative Layout ShiftCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good. (CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good.).” — on the Image component. Jump to quote
- “Your page will be rendered immediately with any specified fallback content as a placeholder. Then, the component’s own contents are fetched on the client and displayed when available.” — on Server Islands. Jump to quote
Jason Miller (creator of Preact, coined “islands architecture”)
- Selective hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. works by “renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. HTML pages on the server, and inject[ing] placeholders or slots around highly dynamic regions.” — cited in Astro Docs: Islands Architecture
Joost de Valk (founder of Yoast SEO)
- “From an SEO perspective, static HTML on a CDN is a better starting point than most CMSes will ever give you.” — Joost.blog: Astro SEO Complete Guide
Astro SEO checklist
A pass to confirm an Astro site is actually set up for search — not just sitting on a good foundation:
-
site:is set inastro.config.mjs(sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. silently won’t generate without it). -
@astrojs/sitemapis installed and the sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. reference is added torobots.txtmanually. - A
robots.txtexists inpublic/and doesn’t block anything you want indexedStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.. - Every page sets a unique
titleanddescription(ideally via a sharedBaseLayout.astro). - A self-referencing canonical is on every page and matches
og:url. - Content Collections use a Zod schema that makes
title/descriptionrequired (build fails if missing). - Images use
<Image />/<Picture />; all havealt(a missing one is a compile error anyway). - The LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good./hero image overrides the default lazy-load with
loading="eager"andfetchpriority="high". - No indexable content lives in a Server Island (
server:defer) — crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. see the fallback, and Server Islands need an adapter to work at all. - No indexable content lives only inside a
client:onlycomponent — it skips server renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM., so there’s no HTML for it until the browser hydrates. - For any route using
output: 'server'or a per-routeprerender = false, confirm runtime-only URLs are added to the sitemap explicitly (customPages) — automatic sitemap discovery targets statically generated routes. - Structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. (JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal.) is in the server-rendered
<head>. - If
<ClientRouter />is enabled, spot-check that each URL still returns full HTML on a direct fetch. - On-demand/server routes: fetch a live production URL directly and confirm the status code, redirectA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't. behavior, and full response HTML — don’t extrapolate from local dev, since adapter/runtime behavior can differ.
The mental models
1. The raw HTML is the finished page — for that route’s default.
Astro’s static output mode means there’s no render wave to wait on for a prerendered
route — what GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. fetches is what gets indexedStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.. That’s a per-route default, not a
site-wide guarantee (output: 'server' and per-route prerender can change it), so
check the route, not just the top-level config. When it applies, View Source is the
truth (the opposite of a CSR SPA) — but confirm it applies before treating it as fact.
2. Foundation, not finish, and not an outcome guarantee. Astro gives you clean HTML and strong performance mechanics for free. Everything that signals meaning to search engines — meta, canonical, sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., schema — is a deliberate step you add. “Good architecture” ≠ “done,” and neither one is proof of crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index., indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., or rankings — those still have to be validated on the deployed site.
3. Islands are additive — except client:only.
Interactivity sits on top of HTML, never under it, for client:load/client:idle/
client:visible: those directives add JS to one component without removing content from
the crawlable base. client:only breaks that pattern — it skips server renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM.
entirely, so it’s not additive, it’s a genuine gap in the static HTML unless you plan for
it. And island hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. is selective, not resumability — don’t conflate the two.
4. The static shell is what crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. see. For Server Islands, the crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. reads the fallback in the static shell, not the deferred content. Decision rule: indexable content goes in the main template; personalized/dynamic content goes in the island.
5. Browser enhancement ≠ structural change.
View Transitions change the browser experience (soft navigation via history.pushState)
but not the crawl experience (each URL is still a full HTML page). Enhancements that
leave the underlying MPA intact are SEO-safe.
6. Validate at build time. Content Collections + Zod turn “remember to add a title” into “the build won’t ship without one.” Push SEO requirements into the type system and they stop being things you can forget.
Astro SEO — cheat sheet
What’s automatic vs. what’s on you
| Concern | Astro does it? | What you do |
|---|---|---|
| Static HTML output | ✅ Default (static mode) | Nothing — it’s the default, but check per-route prerender |
| Non-hydrated components ship no component JS | ✅ Islands (except client:only) | Use client:* only where needed; keep indexable content out of client:only |
| Image WebP + dimensions + lazy | ✅ <Image> | Override LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good. image to eager |
alt enforcement | ✅ Compile error if missing | Write good alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search. |
| SitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. | ⚠️ Plugin, static routes only | astro add sitemap + set site: + customPages for runtime-only URLs |
| SitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. in robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere. | ❌ | Add the line manually |
| Meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored. / canonical | ❌ | BaseLayout.astro props |
| Structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. (JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal.) | ❌ | Add to <head> |
| robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere. | ❌ | File in public/ |
| Outcome guarantee (crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index./rankings/CWVGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data.) | ❌ — mechanisms only | Validate the deployed route yourself |
Output modes
| Mode | Config | SEO (once verified) | Use for |
|---|---|---|---|
| static (default) | — | ✅ Full HTML on first request | Most content; served from CDN edge |
| server | output: 'server' | ✅ Equal to static for crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. | Auth, real-time, personalization |
| Per-route override | export const prerender = false (static default) or = true (server default) | ✅ | Mixing prerendered + on-demand routes |
Islands directives
client:load— hydrate immediately.client:idle— hydrate when the browser is idle.client:visible— hydrate when scrolled into view (best for below-the-fold; protects LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good.).client:only— skips server renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. entirely. No HTML for this component until the browser hydrates it; not additive like the others.
Fast rules
site:missing → no sitemap (silent).- Indexable content in a Server Island → crawler sees the fallback; Server Islands need an adapter.
- Indexable content only inside
client:only→ no HTML for it, period. - Sitemap covers static routes → add runtime-only URLs via
customPages. - View Transitions use
history.pushState→ SEO-safe, MPA underneath. - Static/server output and islands are mechanisms, not guarantees — validate the deployed route, status code, and full HTML before claiming an outcome.
- A fast empty page still doesn’t rank — speed is a signal, not the signal.
Audit Astro’s built HTML
Run this after astro build. It checks the artifact crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. receive, not the source
component tree:
find dist -name '*.html' -type f | while IFS= read -r file; do
canonicals=$(grep -Eio '<link[^>]+rel=["'"']canonical["'"'][^>]*>' "$file" | wc -l | tr -d ' ')
titles=$(grep -Eio '<title>[^<]*</title>' "$file" | wc -l | tr -d ' ')
if [ "$canonicals" -ne 1 ] || [ "$titles" -ne 1 ]; then
printf '%s\ttitles=%s\tcanonicals=%s\n' "$file" "$titles" "$canonicals"
fi
doneAn empty result means every generated HTML file has exactly one title and canonical;
it does not validate whether their values are correct, so sample those separately. This
only checks the build artifact — it says nothing about on-demand (output: 'server')
routes or Server Islands, which don’t exist as static files.
Spot-check live routes in production
For anything that renders on demand — output: 'server' routes, per-route prerender = false, or Server Islands — the build-output check above doesn’t apply. Check the actual
deployed response instead:
# Replace with your real URLs
for url in "https://example.com/" "https://example.com/some-server-route/"; do
echo "== $url =="
curl -sS -D - -o /dev/null "$url" | grep -Ei '^(HTTP|location|cache-control):'
doneLook for the status code you expect (200 for a live page, a real redirectA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't. code if it
redirects — don’t assume 302 vs 301 without checking), and confirm curl -sS "$url"
returns full markup, not a fallback shell, if you’re checking a page that includes a
Server Island. Do this against production, not astro dev — adapter and runtime
behavior can differ from local.
Patrick's relevant free tools
- SEO Incident Simulator — Practice thirty deterministic technical SEO incident investigations — indexability, crawl controls, redirects, sitemaps, markup, caching, DNS, bot verification, rendering, hreflang, and faceted navigation — with clearly labeled fixture evidence and Find → Fix → Verify handoffs.
- Raw vs. Rendered HTML Checker — See what's in your page's initial HTML versus after JavaScript runs — headless-Chrome rendering only when the page actually needs it, a rendering-strategy verdict (SSR / prerendered / CSR / hybrid), ~15 calibrated JavaScript-SEO checks (noindex, canonicals, robots.txt blocking, links, soft 404s), a side-by-side raw-vs-rendered diff, and shareable reports.
- Page Speed Test & Core Web Vitals Checker — One sentence tells you whether a page passes Core Web Vitals and what to fix first — real Chrome user data (CrUX) with a Lighthouse lab fallback, mobile and desktop side by side, loudly-labeled data sources, and prioritized diagnostic fixes. Plus a bulk origin scorecard with CSV export.
Tools for an Astro site
@astrojs/sitemap— the official sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. integration (astro add sitemap). Don’t forgetsite:in the config.astro-seo(npm) — optional community component that bundles title, description, Open GraphOpen Graph (OG) tags are `<meta>` elements in a page's head, defined by the Open Graph protocol (ogp.me, created by Facebook), that describe a page as a shareable object — its title, description, image, URL, and type. They control how a link preview card looks when the page is shared on Facebook, LinkedIn, Slack, Discord, WhatsApp, and iMessage. They are not a direct Google ranking factor, though Google reads og:title, og:image, and og:site_name as inputs to how a result appears., Twitter cardsTwitter Card tags (now often called \"X Cards\") are <meta name=\"twitter:...\"> tags in a page's <head> that tell X's link-unfurling system what rich preview to build for a shared URL — image, headline, description, and (for player cards) an embeddable frame. The four card types are summary, summary_large_image, app, and player. Every twitter:* property falls back to its og:* equivalent if missing — except twitter:card itself, which has no fallback and must be set explicitly. They are not a Google or Bing ranking factor., and canonical into one tag.astro-seo-schema(npm) — typed JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. structured-data helper for Astro.- astro:assets
<Image>/<Picture>— built-in image optimization (WebP/AVIF, dimensions, lazy-load,altenforcement). - URL InspectionA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version. (Google Search ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results.) — confirm your content is in the crawled HTML (with Astro it should be in View Source already — a quick sanity check).
- A JS-renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. — Ahrefs Site Audit or Screaming Frog to verify raw vs. rendered parity across the site (on prerendered routes they should match — confirm it, don’t assume it, and check separately for any on-demand or Server Island routes).
- IndexNowIndexNow is an open push protocol that lets you instantly tell participating search engines (Bing, Yandex, Naver, Seznam, and Yep) which URLs you've added, changed, or removed via a simple HTTP request — and one submission is shared across all of them. Google does not use it. — pair with your deploy/publish step so Bing and Yandex hear about new static pages immediately.
Test yourself: Astro SEO
Five quick questions on how Astro’s architecture affects SEO. Pick an answer for each, then check.
Resources worth your time
My related writing
- JavaScript SEO: A Definitive Guide — the renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. fundamentals behind why Astro’s HTML-first output is such an advantage.
- The Beginner’s Guide to Technical SEO — where framework choice fits in the bigger picture.
My speaking
- How Search Works (SlideShare) — my walkthrough of crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor., renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM., indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., and ranking — the pipeline Astro’s SSG default short-circuits. (Standing disclaimer: “This is my understanding of systems… not going to be 100% complete or accurate.”)
From around the industry
- Astro Docs: Islands Architecture — the canonical explanation of how Astro strips client-side JS.
- Astro Docs: @astrojs/sitemap — official setup, the
site:requirement, andfilter/serialize/i18noptions. - Astro Docs: Server Islands —
server:defer, fallback content, and the client-fetch behavior crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. don’t see. - Joost de Valk: Astro SEO Complete Guide — the most authoritative practitioner guide, by Yoast’s founder; strong on modern AI-ready patterns and IndexNowIndexNow is an open push protocol that lets you instantly tell participating search engines (Bing, Yandex, Naver, Seznam, and Yep) which URLs you've added, changed, or removed via a simple HTTP request — and one submission is shared across all of them. Google does not use it..
- Google Search Central: Understand JavaScript SEO Basics — the render queue, crawlable links, and the History API guidance Astro’s View Transitions follow.
- Astro: 2023 Web Framework Performance Report — the Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. data comparing Astro to other frameworks.
- Search Engine Journal: Core Web Vitals, WordPress and Astro — independent coverage of the Astro vs WordPress performance comparison.
Stats worth citing
All figures below come from Astro’s 2023 Web Framework Performance ReportThe Google Search Console report that shows how your site actually performed in Google Search, built from real impressions and clicks. It reports four metrics — clicks, impressions, average CTR, and average position — and keeps the most recent 16 months of data. (and SEJ’s coverage of it); treat them as 2023-era benchmarks rather than live numbers.
- Over 50% of Astro sites pass Google’s Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. Assessment — above the industry average of ~40.5%, and Astro and SvelteKit were the only major frameworks beating that baseline (Next.js ~25%, Nuxt ~20%). Source
- INPInteraction to Next Paint — the input-to-paint latency at the 75th percentile of a page's interactions. ≤200 ms is good. pass rate of 68.8% for Astro — credited to the MPA architecture (no JS-driven navigation), which keeps the main thread free. Source
- Median page weight of 1.65 MB — the lightest in the dataset. Source
- LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good.: Astro ~0.44s vs WordPress ~0.81s — roughly 46% faster, in the report’s comparison. Coverage
Astro SEO
Astro SEO is the practice of optimizing sites built with the Astro web framework for search. Astro prerenders to static HTML by default, so content is in the raw HTML on first crawl for that route — no rendering queue — which makes it a strong starting point for SEO, though the default can be overridden per route and doesn't guarantee crawlability or rankings on its own.
Related: JavaScript SEO, Headless CMS SEO
Astro SEO
Astro SEO is the set of technical and on-page practices for optimizing websites built with the Astro web framework for crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index., indexability, and performance. What makes Astro distinctive is its architecture: its default output mode prerendersThe Speculation Rules API is a Chromium browser API (Chrome/Edge 109+) that lets a site tell the browser which same-site pages to prefetch (download the HTML document) or prerender (fully load and render in an invisible tab) before a visitor clicks — so the next navigation can be near-instant. It's a browser-side performance feature for real users, not a crawling, indexing, or ranking signal. pages and endpoints to static HTML at build time. That’s a default, not a universal property — output: 'server' flips it, and individual routes can override the default with prerender. Where it applies, a crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. sees your full content in the raw HTML on the first fetch — there’s no renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. queue to wait on, the way there is with client-rendered React or Vue apps.
Astro’s “islands architecture” hydrates JavaScript only for components marked with a client:* directive; a component without one contributes HTML without its own hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. JS, though Astro can still ship page scripts, other islands, and router enhancements elsewhere on the page — so “zero JS” describes a component, not a blanket property of the page. (client:only is the exception: it skips server renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. entirely.) Each page is its own URL in a multi-page-app (MPA) structure, so there’s no SPA routing to hijack discovery. Optional features layer cleanly on top: the @astrojs/sitemap integration generates sitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. from statically generated routes (runtime-only URLs need explicit customPages), the astro:assets <Image> component produces WebP and sets dimensions to avoid layout shiftCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good., and View Transitions add SPA-like navigation using the History API while leaving each page independently crawlable.
The catch is that “good architecture” is not the same as “done” — or a guarantee. Astro generates clean HTML, but it does not write your meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored., canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content., sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., or structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. for you, and static or server output, islands, and adapters don’t by themselves prove crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index., indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., rankings, or Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. — validate the deployed route. Astro is the best possible foundation, not a finished SEO solution.
Related: JavaScript SEO, Headless CMS SEO
Build-time retrieval analysis plus live signals for this exact article. The automatic chunk report includes a deterministic readiness score and is ready without a model download.
Search Console
sampleGA4 traffic (28d)
sampleCloudflare traffic (7d)
sampledCrUX field data (28d, phone)
sampleGoogle NLP entities
localChangelog
Revision history
Compare the published article with an archived editorial snapshot. Added and removed words are shown only after you open a comparison.
Updated Jul 17, 2026.
Editorial summary and recorded change details.Summary
Qualified Astro's rendering advantages and documented the route-level conditions that determine what crawlers actually see, following an editorial review against current Astro docs.
Change details
-
Distinguished Astro's static default from server-rendered and per-route overrides (prerender true/false), with guidance to verify deployed responses instead of treating framework defaults as guarantees.
-
Clarified selective hydration, client:only's server-rendering skip, and the distinction between islands and resumability; noted that page scripts and router JS can ship outside islands, so 'zero JS' is per-component, not page-wide.
-
Expanded sitemap and Server Island guidance to cover runtime-only routes (customPages), the adapter requirement for Server Islands, and production spot-checks for status codes and full HTML on on-demand routes.
-
Added an explicit no-outcome-guarantee framing throughout (static/server output, islands, and adapters are mechanisms, not proof of crawlability, indexing, rankings, or Core Web Vitals) and two new anti-patterns covering per-route assumptions and client:only misuse.