← All framework guides

Astro SEO: 8 checks and how to fix them

These are the Astro-specific checks RankCLI runs, and the fix for each one in Astro's own idiom. Astro renders on the client by default, which is where most of its SEO problems come from.

8 checks
1 rated error
static-first
Client-rendered by default

Run all of these against your own site, free and without an account:

npx @rankcli/cli audit -u https://your-site.dev

What Astro gives you for free

  • •Zero JavaScript by default
  • •Static HTML output
  • •Excellent performance (100 Lighthouse)
  • •Content Collections for organized content
  • •Built-in sitemap and RSS integrations
  • •Partial hydration (Islands Architecture)

The checks

warning
ASTRO_NO_SITEMAP

Missing sitemap integration

Astro has a built-in sitemap integration.

Add the sitemap integration:

npx astro add sitemap

Configure in astro.config.mjs:

import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://yoursite.com',
  integrations: [sitemap()],
});
error
ASTRO_NO_SITE_CONFIG

Missing site URL configuration

The site URL is required for sitemap and canonical URLs.

Add site to astro.config.mjs:

export default defineConfig({
  site: 'https://yoursite.com',
});
warning
ASTRO_CLIENT_DIRECTIVE_OVERUSE

Overusing client: directives

Too many client-side components reduce Astro's performance benefits.

Only hydrate components that need interactivity:

<!-- Bad - unnecessary JS -->
<StaticCard client:load />

<!-- Good - no JS for static content -->
<StaticCard />

<!-- Good - only hydrate when needed -->
<InteractiveWidget client:visible />

Prefer client:visible over client:load for below-fold content.

notice
ASTRO_NO_BASE_LAYOUT

Pages missing base layout

Using a shared layout ensures consistent SEO tags.

Create a BaseLayout.astro:

---
// src/layouts/BaseLayout.astro
interface Props {
  title: string;
  description: string;
}
const { title, description } = Astro.props;
---
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>{title}</title>
    <meta name="description" content={description} />
    <link rel="canonical" href={Astro.url} />
  </head>
  <body>
    <slot />
  </body>
</html>
notice
ASTRO_NO_ASTRO_SEO

Not using @astrolib/seo

The astro-seo package provides comprehensive SEO components.

Install astro-seo:

npm install astro-seo
---
import { SEO } from 'astro-seo';
---
<SEO
  title="Page Title"
  description="Description"
  openGraph={{
    basic: {
      title: "OG Title",
      type: "website",
      image: "/og-image.jpg",
    }
  }}
  twitter={{
    creator: "@handle"
  }}
/>
warning
ASTRO_IMAGE_NOT_OPTIMIZED

Images not using Astro Image

Use Astro's built-in Image component for optimization.

Use the Image component:

---
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---
<Image
  src={heroImage}
  alt="Hero"
  width={1200}
  height={630}
  loading="eager"
/>
notice
ASTRO_NO_CONTENT_COLLECTIONS

Not using Content Collections

Content Collections provide type-safe content with built-in frontmatter validation.

Define a collection in src/content/config.ts:

import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.date(),
  }),
});

export const collections = { blog };
notice
ASTRO_SSR_OVERUSE

Using SSR when static would work

Static pages are faster and cheaper to host.

Use prerender for pages that don't need SSR:

---
export const prerender = true; // Static generation
---

Or set default to static in config:

export default defineConfig({
  output: 'hybrid', // Static by default, SSR opt-in
});

Check your own Astro site

280+ checks including every one above. No signup, nothing leaves your machine.

Run a free audit