Astro SEO Checklist: 12 Settings to Configure, With Code

Yes, Astro is good for SEO because it ships static HTML with little JavaScript, but you still set up meta tags, canonicals, sitemap, JSON-LD and redirects.

Yes, Astro is good for SEO because it ships static HTML with little JavaScript, but you still set up meta tags, canonicals, sitemap, JSON-LD and redirects.

Harish Malhi - founder of Goodspeed

Harish Malhi

•

•

•

•

Calculating...

Short answer

Yes, Astro is good for SEO because it ships fast, static HTML with very little JavaScript, so crawlers and AI agents see the full page. But you still need to set up titles and meta tags, canonicals, a sitemap, robots.txt, JSON-LD, redirects, trailing slash rules, hreflang, image optimization and CMS fields for editors.

Key takeaways

A 12-item Astro SEO checklist with code for the head component, sitemap and JSON-LD, plus who owns each item.

Table of Contents

Astro SEO Checklist: 12 Settings to Configure, With Code – Goodspeed Studio blog

Yes, Astro is good for SEO. It renders pages to plain HTML at build time and ships almost no JavaScript unless you ask for it, so search engines and AI crawlers get complete, fast pages. That is the foundation. It is not the finished job. Astro has no built-in meta tags, no default sitemap, no structured data and no SEO fields for your editors. Someone has to add them.

Most Astro SEO guides are written for developers starting a blog. This one is for the marketing lead who owns the site, with a developer reading over your shoulder. It covers what Astro handles, what it leaves to you, who should own each piece, and where Astro sites quietly lose rankings.

Is Astro good for SEO?

Astro's default output is static HTML. Every page is built ahead of time, and interactive components only load JavaScript when they are marked to (Astro calls these "islands"). For search, that means three things:

  • Crawlers see the full page. Content is in the HTML on first load, not assembled later by JavaScript.

  • Pages are light. Less JavaScript usually means better Core Web Vitals, especially Interaction to Next Paint on mobile.

  • Hosting is simple. Static files on a CDN are fast and hard to break.

Compare that with a single-page React app, where content can depend on client-side rendering, or a WordPress site carrying a stack of plugins. Astro starts ahead. If you are weighing frameworks, our Astro vs Next.js and Astro vs WordPress comparisons go deeper.

The catch: a fast page with a missing canonical, no sitemap and a generic title still ranks badly. Astro is a framework, not an SEO plugin.

The Astro SEO checklist

Item

How in Astro

Who owns it

Title and meta description

A reusable <SEO> head component, fed by CMS fields

Dev builds, marketing writes

Canonical URL

Built from Astro.url and the site config value

Dev

XML sitemap

@astrojs/sitemap integration (needs site set)

Dev

robots.txt

Static file in public/ or a robots.txt.ts endpoint

Dev, marketing approves

Structured data (JSON-LD)

<script type="application/ld+json"> in the head component

Dev builds, marketing chooses types

Open Graph and social images

OG tags in the head component, image per page or generated

Dev builds, marketing supplies

Redirects

redirects in astro.config.mjs or host-level rules

Dev, marketing supplies the map

Trailing slash consistency

trailingSlash config plus matching host settings

Dev

hreflang for multiple languages

i18n routing plus manual <link rel="alternate"> tags

Dev

Image optimization

<Image /> and <Picture /> from astro:assets

Dev builds, marketing writes alt text

RSS feed

@astrojs/rss endpoint

Dev

Editor-managed SEO fields

Fields in your headless CMS mapped to the head component

Dev builds, marketing uses

If you only remember one thing from this table: every row marked "marketing" should be editable without a developer. That is a build decision, not a tool limitation.

Titles, meta tags and canonicals

Astro has no global SEO settings panel. The standard pattern is one head component that every layout uses. Here is a minimal version.

---
// src/components/SEO.astro
interface Props {
  title: string;
  description: string;
  image?: string;
  noindex?: boolean;
}
const { title, description, image = '/og-default.png', noindex = false } = Astro.props;
const canonical = new URL(Astro.url.pathname, Astro.site);
const ogImage = new URL(image, Astro.site);
---
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
{noindex && <meta name="robots" content="noindex, nofollow" />}
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonical} />
<meta property="og:image" content={ogImage} />
<meta name="twitter:card" content="summary_large_image"

---
// src/components/SEO.astro
interface Props {
  title: string;
  description: string;
  image?: string;
  noindex?: boolean;
}
const { title, description, image = '/og-default.png', noindex = false } = Astro.props;
const canonical = new URL(Astro.url.pathname, Astro.site);
const ogImage = new URL(image, Astro.site);
---
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
{noindex && <meta name="robots" content="noindex, nofollow" />}
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonical} />
<meta property="og:image" content={ogImage} />
<meta name="twitter:card" content="summary_large_image"

---
// src/components/SEO.astro
interface Props {
  title: string;
  description: string;
  image?: string;
  noindex?: boolean;
}
const { title, description, image = '/og-default.png', noindex = false } = Astro.props;
const canonical = new URL(Astro.url.pathname, Astro.site);
const ogImage = new URL(image, Astro.site);
---
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
{noindex && <meta name="robots" content="noindex, nofollow" />}
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonical} />
<meta property="og:image" content={ogImage} />
<meta name="twitter:card" content="summary_large_image"

Two things to check with your developer. First, site must be set in astro.config.mjs, or Astro.site is undefined and your canonicals and sitemap break. Second, the canonical should match your trailing slash rule exactly (more on that below). Community packages like astro-seo wrap this pattern, but a 20-line component you own is easier to audit.

Sitemap and robots.txt

The official @astrojs/sitemap integration is the default choice. Install it with npx astro add sitemap, then set your domain:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://www.example.com',
  trailingSlash: 'always',
  integrations: [
    sitemap({
      filter: (page) => !page.includes('/thank-you/'),
    }),
  ],
});
// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://www.example.com',
  trailingSlash: 'always',
  integrations: [
    sitemap({
      filter: (page) => !page.includes('/thank-you/'),
    }),
  ],
});
// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://www.example.com',
  trailingSlash: 'always',
  integrations: [
    sitemap({
      filter: (page) => !page.includes('/thank-you/'),
    }),
  ],
});

It outputs sitemap-index.xml plus numbered files like sitemap-0.xml. Point robots.txt and Google Search Console at the index file. The filter option keeps thank-you pages, drafts and gated content out.

One limit worth knowing: per the Astro sitemap docs, the integration cannot generate entries for dynamic routes rendered on demand (server-side). If parts of your site are server-rendered, those URLs need a custom sitemap endpoint.

For robots.txt, a static file in public/ works. If you have staging and production builds, a src/pages/robots.txt.ts endpoint lets you block crawlers on staging automatically.

Structured data (JSON-LD)

Astro has no schema helper, which is fine. You add JSON-LD as a script tag. Use set:html so Astro outputs the JSON without escaping it:

---
const schema = {
  '@context': 'https://schema.org',
  '@type': 'Article',
  headline: post.title,
  datePublished: post.publishedAt,
  author: { '@type': 'Person', name: post.author },
};
---
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
---
const schema = {
  '@context': 'https://schema.org',
  '@type': 'Article',
  headline: post.title,
  datePublished: post.publishedAt,
  author: { '@type': 'Person', name: post.author },
};
---
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
---
const schema = {
  '@context': 'https://schema.org',
  '@type': 'Article',
  headline: post.title,
  datePublished: post.publishedAt,
  author: { '@type': 'Person', name: post.author },
};
---
<script type="application/ld+json" set:html={JSON.stringify(schema)} />

Start with Organization on the homepage, Article or BlogPosting on posts, BreadcrumbList on deep pages, and FAQPage only where the visible page has a real FAQ. Generate the values from CMS data, not hand-typed strings, so the schema never drifts from the page.

Images, redirects, trailing slashes and i18n

Images. Use <Image /> and <Picture /> from astro:assets. According to the Astro images guide, they convert to modern formats like WebP, infer width and height to prevent layout shift, and lazy-load by default. alt is required, which is a useful forcing function. Remote images from a CMS are only optimized if their domain is authorized in image.domains or image.remotePatterns. Responsive srcset output is available through the layout option (added in Astro 5.10).

Redirects. Astro's redirects config maps old paths to new ones, including dynamic patterns like '/blog/[...slug]': '/articles/[...slug]'. Read the fine print in the configuration reference: on a fully static build with no adapter, Astro writes a <meta http-equiv="refresh"> page, not a real 301. Google generally follows these, but for a migration you want true 301s. Put them in your host's redirect rules (Netlify, Vercel, Cloudflare) or use an adapter.

Trailing slashes. The trailingSlash default is 'ignore', which means /pricing and /pricing/ can both resolve. That is two URLs for one page. Pick 'always' or 'never', match build.format, match your host settings, and make sure canonicals and internal links agree.

Multiple languages. Astro's i18n routing handles locale folders and gives you helpers like getRelativeLocaleUrl(). It does not output hreflang tags for you. Your head component needs <link rel="alternate" hreflang="..."> for each translation plus x-default. The sitemap integration has an i18n option that adds alternates to the sitemap too.

RSS. @astrojs/rss creates a feed from a content collection. It is not a ranking factor, but feeds help discovery and syndication and take minutes to add.

The part most builds miss: SEO fields for editors

Here is where Astro projects most often fall short for marketing teams. The developer hard-codes titles in page files, ships, and leaves. Six months later, marketing cannot change a meta description without a pull request.

Fix this at build time. In your headless CMS, every page and post type should have: SEO title, meta description, OG image, canonical override, noindex toggle and, ideally, a slug field with redirect creation when it changes. Astro's content layer pulls those fields through loaders and type-checks them with a schema, so a missing description can fail the build instead of shipping blank. Our guide to choosing an Astro CMS covers which platforms make this easiest.

If your team is about to rebuild on Astro, or you have an Astro site that only a departed developer understands, this is the work we do. Goodspeed is an AI-native website studio that builds with the best tool for the job, and that includes Astro sites with headless CMSs. We are a Framer Enterprise Partner with 300+ products shipped and a 5.0 Clutch rating. We set up the SEO layer so your marketing team runs it, not your engineers.

Astro SEO for AI search

AI assistants and answer engines read pages much like crawlers do, and many of them do not run JavaScript. Astro's static HTML is a real advantage here. To make the most of it:

  • Keep content in the HTML. Do not load key copy, pricing or FAQs through client-side components.

  • Use structured data. It gives machines clear facts about who you are and what a page covers.

  • Write answer-first. Lead sections with a direct answer, then the detail. Clear H2s phrased like real questions help.

  • Treat llms.txt as optional. It is easy to add as a static file, but there is no strong public evidence that major AI search products rely on it. Do it if it is cheap, not instead of the basics.

Where Astro SEO breaks

  • Content rendered only on the client. A React island with client:only renders nothing on the server. If your testimonials, pricing table or FAQ live there, crawlers may not see them.

  • Missing redirects after migration. The most common way to lose traffic. Every old URL with links or rankings needs a 301 to its closest new match, live on launch day.

  • Rebuild delay. With build-time content collections, a CMS edit does not appear until the site rebuilds. Editors fix a title, see no change and assume it failed. Set up a CMS webhook that triggers a deploy, or use Astro 6's live content collections (on-demand rendering required) for content that must update instantly.

  • Duplicate URLs from trailing slashes. The 'ignore' default, plus inconsistent internal links, creates two versions of every page.

  • Unoptimized images. Plain <img> tags and unauthorized remote CMS domains skip astro:assets entirely. Large hero images will hurt Largest Contentful Paint.

  • Missing site value. Canonicals and the sitemap quietly break without it.

A note on migrating to Astro

Moving to Astro can lift performance, but the move itself is where rankings are won or lost. Before launch, crawl the old site, export every indexed URL, map each one to a new destination, carry over titles, descriptions and schema, and test redirects on staging. After launch, watch Search Console coverage and 404s daily for the first few weeks. We cover the full process for WordPress to Astro and Webflow to Astro.

Astro is the base, not the strategy

Astro gives you fast, clean HTML that search engines and AI crawlers can read. Everything else on the checklist, from canonicals to redirects to editor-owned meta fields, is a set of decisions your build either makes well or misses. Get them right once and your marketing team can run SEO without filing tickets.

If a launch, funding round or redesign is about to put more eyes on your site, get the foundation checked before the traffic arrives. Book a 30-minute project evaluation →

Frequently asked questions

Is Astro good for SEO?
Yes. Astro outputs static HTML with minimal JavaScript by default, which makes pages fast and fully crawlable. You still need to add meta tags, canonicals, a sitemap and structured data yourself.
Does Astro generate a sitemap automatically?
Not by default. Add the official @astrojs/sitemap integration with npx astro add sitemap and set the site value in astro.config.mjs. It outputs sitemap-index.xml for static and prerendered routes.
How do I add meta tags in Astro?
Create a reusable SEO head component that takes a title, description and image as props, and use it in every layout. Feed those props from CMS fields so editors can change them.
How do I add structured data or JSON-LD in Astro?
Add a script tag with type application/ld+json and pass JSON.stringify of your schema object through set:html. Generate the values from your content data so schema matches the visible page.
How should I handle canonical URLs in Astro?
Build the canonical from Astro.url.pathname and Astro.site in your head component. Make sure the site config value is set and that the URL format matches your trailingSlash setting.
Do Astro redirects return a 301?
On a static build without an adapter, Astro creates a meta refresh page rather than a real 301. For migrations, configure 301s at your host or use an adapter that supports status codes.
Does Astro add hreflang tags for multilingual sites?
No. Astro's i18n routing handles localized URLs, but you need to add hreflang link tags in your head component. The sitemap integration can also list language alternates.
Why don't my CMS edits show on my Astro site?
Build-time content collections only update when the site rebuilds. Connect a CMS webhook to trigger a deploy, or use live content collections with on-demand rendering for content that must update instantly.

About the Author

About the Author

Harish Malhi - founder of Goodspeed

Harish Malhi

Founder of Goodspeed

Harish Malhi is the founder of Goodspeed. He left Google in 2019 to build his own product, and started Goodspeed to give other founders the same head start. Today the studio designs and builds websites and the whole digital experience around them for funded tech companies, at the moments that matter: a raise, a launch, a press cycle. Goodspeed is a Framer Enterprise Expert, was Bubble’s Agency of the Year in 2024, and has shipped 300+ products, including launch pages for an NVIDIA-backed AI lab and a Webby-nominated site for a Boeing-backed aviation company. It holds a 5.0 rating from 46 reviews on Clutch.