Astro SEO depends on the HTML, URLs and responses your site actually publishes. Installing a metadata component or sitemap integration does not verify that the right content reaches a crawler, that canonicals represent the right pages, or that a new CMS record appears after deployment.
These 21 checks cover those release decisions for Astro blogs, documentation and marketing sites. Start with a homepage, a content page, an archive page and an intentionally missing URL, then repeat the relevant checks across your templates. The examples are illustrative and based on official documentation and package source checked on November 1, 2026; they are not results from a tested customer deployment.
1. Give each head element one owner
Inspect the shared layout, theme head component and installed packages before adding another SEO component. The community astro-seo component is optional; its presence in a dependency list does not prove that your layout renders it or passes the right props.
Read the generated head for two different routes and assign ownership:
| Element | Passing result |
|---|---|
| Title | One intentional page title |
| Description | One useful description for this page |
| Canonical | One preferred URL, with no conflicting second owner |
| Robots | A deliberate policy across meta tags and response headers |
| Structured data | Consistent entities that describe the visible page |
Check the version in the lockfile and deployment log. The package-specific examples below refer to astro-seo 1.2.0, whose source was inspected, rather than every package with “SEO” in its name. Keep the owner you already use when it meets the requirement. Installing another component can duplicate output without correcting the data flowing into it.
2. Make titles identify the actual page
Compare the final <title> on your homepage, a post and an archive. Each should tell a visitor which page they have reached. A title stored in front matter is insufficient if a layout ignores it and renders the same site name everywhere.
For example, a documentation hub might use “Documentation | Example”, while its specific guide uses “Connect a Custom Domain | Example”. These are illustrative titles, not a required naming formula. Apply a brand suffix once, accounting for any titleTemplate your component already uses.
Check the visible H1 too. It should identify the same subject and lead into a logical heading hierarchy. Matching the title wording is acceptable when it reads naturally; inventing a different phrase solely to avoid repetition adds no value.
Google's title-link guidance emphasizes clear, descriptive titles and explains that search presentation can use other page signals. Fix blank, boilerplate and misleading output rather than treating a character count as a guaranteed display width or ranking rule.
3. Check descriptions after layout mapping
Open the HTML response and read the description as a sentence. Does it explain this page's useful answer, or repeat the homepage description? A blog index and a detailed installation guide should not inherit an indistinguishable summary simply because both use the same layout.
In astro-seo 1.2.0, a description tag is rendered when the description prop has a value. The component does not discover an arbitrary seoDescription field in your CMS for you. Follow the field through the page, layout and head component, checking spelling and any defaults at each boundary.
Use a concise, factual summary rather than a keyword list. For an installation guide, describe what the reader can configure and verify; for an archive, describe the material it organizes. Check the final output after any formatting or fallback logic.
Google may generate a snippet from page content instead of the supplied description. Successful implementation means your intended tag is present and accurate, not that every search result must display it verbatim.
4. Keep the main answer in the page response
Fetch the document before assuming that what you see after JavaScript runs is what the initial HTML contains. Check the main heading, answer, article body and important links. A useful content page should not initially be only an empty application container or a loading message.
Astro's directive reference distinguishes these cases:
| Rendering choice | What to verify |
|---|---|
| Normal server-rendered content | The useful text is in the page response |
| A hydrated UI component | Server HTML remains useful before interaction |
client:only |
The component skips server rendering; do not put the only copy of the article here |
server:defer |
Initial fallback content differs from the separately requested island content |
Server islands run in their own request context. Their Astro.url can point at /_server-islands/..., not the containing article. Keep the page's primary metadata in the containing document, rather than deriving its canonical from that island URL.
This is a delivery check, not a claim that Google never renders JavaScript. It reduces avoidable dependence on later rendering for the information the page exists to publish.
5. Generate only eligible published routes
Write down which records may become public before generating routes. Include draft status and publication time in that decision, then apply the same rule to page lists, feeds and any sitemap inventory.
For records whose schema provides a boolean draft and a Date-valued publishedAt, an illustrative predicate is:
const isPublic = (post, now) =>
!post.data.draft && post.data.publishedAt.getTime() <= now.getTime();
Apply it where your real content source is queried. For prerendered dynamic routes, inspect the paths returned by getStaticPaths(). For on-demand pages, check the record's eligibility at request time before rendering it. Installing an adapter does not decide your publication rules.
Keep one approved record, one draft and one future-dated record as checks of the policy. The approved page should be reachable; the other two should not leak through an alternative route or cached list.
A static build evaluates dates when it runs. A scheduled publication time arriving later does not, by itself, rebuild the site. Confirm the actual publishing trigger and use dates with an explicit timezone when a precise release time matters.
6. Set an explicit canonical URL policy
Compare the preferred public URL, rendered canonical and final response destination. They should describe the same page, including the intended domain, deployment base and trailing-slash format. Configure Astro's site, base and URL options against the real host rather than a local preview.
One package detail deserves attention: astro-seo 1.2.0's source constructs its default canonical using both Astro.url.pathname and Astro.url.search. It can therefore preserve query parameters. Do not assume installing the component removes tracking parameters.
For a page where query parameters only track visits and do not change the content, this is an illustrative fragment for an existing head component:
---
if (!Astro.site) throw new Error('Configure the public site URL first.');
const canonical = new URL(Astro.url.pathname, Astro.site);
---
<SEO canonical={canonical.href} />
Keep your existing SEO import. The example deliberately omits the search string; it is inappropriate when a parameter identifies distinct content. Decide that case separately. Also verify encoded paths and slash behavior from real output instead of applying a universal decode or string replacement.
A canonical is a preference signal, not a redirect. Screpy's canonical guide explains the underlying duplicate-content decision.
7. Keep pagination as distinct reachable pages
Open the second archive page directly. It should show the next set of entries, have working previous/next links and identify its own preferred URL. A canonical pointing every page at page one can contradict the fact that each page lists different content.
Google's pagination guidance recommends a separate canonical for each page in the sequence. Do not add noindex to all paginated pages merely because an example checklist does so. Choose exclusions only when they match a deliberate policy for that archive.
If pagination uses a query such as ?page=2, the canonical logic from check 6 must preserve the meaningful distinction. Tracking-only query removal and page selection are different cases. If it uses /blog/2/, verify that the generated canonical retains that path.
Check ordinary anchor links in the response, not just an infinite-scroll button. Astro's pagination data can help build navigation, but the finished archive still needs to expose useful entries and reachable destinations. A rel="next" tag alone is not a replacement for that navigation.
8. Verify the final index directive
Inspect both robots meta elements and X-Robots-Tag response headers on a page intended for search. A preview default, inherited prop or host rule can leave noindex on an otherwise complete production page.
The astro-seo component emits a robots meta element with index, follow defaults and supports a boolean noindex prop. For a deliberately public utility page that should stay out of search, an existing component invocation can include:
<SEO title="Search results" noindex={true} />
This is an illustrative prop fragment, not a private-page security mechanism. Confirm that another head component or response header does not supply a conflicting policy. Setting a custom noindex field in content data has no effect unless your rendering code passes it to the correct owner.
Google needs to crawl a page to read its noindex directive. Do not block that same URL in robots.txt while expecting Google to discover the directive. Authentication controls private access; a search directive does not.
9. Check crawl rules separately from index rules
Fetch robots.txt from the public origin and read the rules that apply to the crawler you care about. For a project hosted under /docs/, a file at /docs/robots.txt does not become the origin's robots policy. Confirm the actual root file and its scope.
Check one important page and the resources needed to render it. A broad Disallow: / left over from staging can prevent useful crawling, while a rule aimed at an internal route can accidentally match a public path. Retain intentional restrictions rather than replacing the entire file with a generic example.
Google's robots.txt documentation distinguishes crawling from access protection and indexing. A blocked URL can still be known from links, and private material needs real access control.
Do not add a fashionable list of AI user agents as an assumed Astro SEO requirement. Crawler permissions should follow the site's actual publishing policy. Keep a record of the exact file, applicable user-agent group and tested path so a future rule change can be reviewed concretely.
10. Compare sitemap eligibility with page eligibility
Compare the public pages you intend to advertise with the URLs in the deployed sitemap. Explain both differences: eligible pages missing from the XML, and unwanted entries still present in it. Matching totals are insufficient because a missing guide and an unwanted draft can cancel each other out.
The official @astrojs/sitemap integration generates output during the build. It does not automatically enumerate every unknown on-demand route or understand arbitrary CMS draft fields. A runtime article can become public while an older build's sitemap remains unchanged.
Use the same publication and canonical policy established in checks 5 and 6. Sample an approved page, a deliberately excluded page and a newly published page, then compare their real responses with XML membership.
Screpy's dedicated Astro sitemap checks cover route inventories, custom URLs, translations, child files and deployed XML validation in detail. Use that workflow for the sitemap implementation; this broader release check establishes that the page policy and discovery file agree. Removing a sitemap entry does not remove the page from search or protect it from visitors.
11. Preserve internal links under the deployment base
Follow navigation, breadcrumbs and contextual links on the deployed site. A hardcoded /guides/setup/ points at the domain root, which can miss a site deployed under /docs/ even when its canonical is correct.
Astro exposes the configured base through import.meta.env.BASE_URL. For a project whose preferred paths end in slashes, an illustrative anchor fragment is:
---
const guideHref = `${import.meta.env.BASE_URL.replace(/\/$/, '')}/guides/setup/`;
---
<a href={guideHref}>Set up your documentation</a>
Use the real route and adapt the suffix to your URL policy. This produces /docs/guides/setup/ for a /docs base and avoids adding that base twice. External destinations should retain their own intended URLs.
Check that important pages have ordinary anchors with real href values in useful places. Google's link guidance explains crawlable links and descriptive text. A sitemap is not a substitute for a visitor being able to reach a guide from its relevant hub. Save one working base-path example and one old-path correction as evidence.
12. Inspect real redirect responses
When a slug changes, fetch the old public URL and record its initial status, destination and final page. The destination should be the relevant replacement, not automatically the homepage. Update important internal links to that destination as well.
Astro's redirect behavior depends on output and adapter support. A static build without an adapter can emit an HTML page with a meta refresh. A supported server or hosting integration can provide an HTTP redirect. A redirects configuration entry alone does not establish which response your production host sends.
For each important move, require evidence from the old URL: the intended permanent or temporary behavior, the correct Location when an HTTP redirect is used, and the expected final content. Check for loops, unnecessary chains and a second redirect caused by slash normalization.
Do not change all routes or migrate the site merely to satisfy this check. Resolve the particular hosting or adapter behavior that fails the intended move, and retain the old-to-new URL mapping for the next release.
13. Return errors for missing content
Request a made-up content URL that is not a valid page. It should return an appropriate error response and a helpful error page, rather than a 200 response containing “not found” or a copy of the homepage.
For an on-demand .astro page, set Astro.response.status = 404 before rendering the missing-record view. Make sure the template then shows that error view instead of continuing into normal article markup with empty data. Astro's response API applies to on-demand responses; a prerendered file needs the host's actual error-page behavior checked separately.
Google treats response statuses differently. Adding noindex to a custom error design does not turn a successful HTTP response into a 404. Likewise, redirecting every missing slug to /404/ is not proof that the original request received the intended error status.
Keep a known-good URL beside the negative check. It should still return the expected content, so a broad rewrite or hosting fallback cannot appear successful merely by displaying the same page for every request.
14. Fetch the social preview image itself
Read the actual og:image and Twitter image URLs from the generated head, then request those assets without relying on an authenticated CMS session. Confirm an image response, sensible dimensions and a clear preview that represents the page. A valid-looking filename can still lead to an expired link or an HTML error screen.
When supplying openGraph to astro-seo 1.2.0, its basic object requires a title, type and image. Pass the values deliberately; a body image or an unrelated CMS field does not automatically satisfy that contract. Check the final Open Graph URL against the preferred page URL too.
Use an absolute public image URL where the sharing consumer needs one, with descriptive social-image alt text when supported. Avoid a private preview hostname and verify any deployment base in the asset path.
An article-specific preview can be useful, but an accurate reusable site image can also be appropriate. The check is whether the published metadata resolves to a usable intended asset. A social card is not evidence that the page is indexed or that the product shown in an image was tested.
15. Check body image meaning and dimensions
Inspect the images visitors actually use to understand the page. Does each informative diagram have an accurate description? Does its rendered size remain readable? Are dimensions reserved so loading it does not unexpectedly move the surrounding content?
For a real local asset, Astro's image component can be used like this:
---
import { Image } from 'astro:assets';
import diagram from '../assets/domain-setup.png';
---
<Image src={diagram} alt="Custom domain pointing to the documentation site" />
The path and description are illustrative; substitute an asset that exists in your project. Astro's image guide distinguishes imported source assets, files in public/ and remote images, which have different processing and dimension requirements. Do not assume every Markdown image or CMS URL receives identical optimization.
Follow the generated src and srcset destinations on the public site. Check alt text in the body independently of social-image alt metadata; one does not populate the other. Google's image guidance supports descriptive context and accessible assets, rather than keyword lists. Keep decorative images from competing with genuinely useful content.
16. Audit hydration and critical loading costs
Review which components need browser JavaScript and when they become useful. An article body does not need hydration merely because it sits beside an interactive calculator. A below-the-fold widget may be a candidate for client:visible; an immediately needed control may need a different directive. Choose based on the experience, not a rule that every island should be delayed.
Check the largest visible content, image loading, fonts and third-party scripts on representative pages. Astro's defaults cannot prevent an oversized hero, unnecessary font families or a heavy embed from slowing the actual experience. Avoid delaying the image or content that visitors need first.
Use current Core Web Vitals guidance: LCP, INP and CLS measure different aspects of loading, interaction and stability. A lab score is useful diagnostic evidence; available field data describes real users under different conditions. Neither a perfect score nor choosing Astro guarantees rankings.
Change one material cause, then compare the same page under comparable conditions. Save the method and observed values. Do not present a framework's marketing claim or somebody else's benchmark as a measured gain on your site.
17. Serialize truthful structured data safely
Inspect existing JSON-LD before adding another script. Select a type that fits the page and compare its claims with visible content. An article should not become a Product or FAQ merely to seek a search feature.
Astro's set:html inserts raw content without automatically escaping it. Serialize a structured object rather than concatenate CMS strings into script markup. This minimal head fragment demonstrates the serialization boundary:
---
const data = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: Astro.props.title,
};
const jsonLd = JSON.stringify(data).replace(/</g, '\\u003c');
---
<script type="application/ld+json" set:html={jsonLd} />
Use the actual page title and complete the chosen type with truthful relevant fields, such as its URL, author, image and dates. Escaping literal less-than signs prevents a string containing </script> from ending the script element early; the JSON parser still recovers the original text. This does not make arbitrary HTML safe to inject elsewhere.
Validate the parsed object and compare it with Google's Article guidance when the page is an article. Syntax validity and search-feature eligibility are separate checks, and neither guarantees a rich result.
18. Keep authors and dates consistent
Compare the visible byline and publication date with the values in structured data and social article metadata. They should refer to the same actual author and event. A CMS account name is not proof that a person wrote or reviewed a particular article.
Use a real author profile where one exists, and display dates in a way readers can understand. Check timezone handling around midnight: a timestamp can belong to different calendar days in different locales. Do not accidentally turn a future scheduled record into today's published article through formatting alone.
A meaningful editorial update may justify a modified date. Rebuilding unchanged content does not. Keep the original publication date when refreshing a page, and ensure any visible “updated” label corresponds to the change it claims.
The metadata validation in check 17 asks whether the object is structurally appropriate; this check asks whether the identity and time claims are true. Avoid invented expert-review labels, credentials and freshness claims even when the template makes them easy to display.
19. Publish language alternates only for real equivalents
Skip this check when the site has one language. A locale in configuration does not create a translated page, and a language homepage is not an equivalent of every article.
For a real translated pair, compare the page content, language labels, canonicals and alternate links in both directions. Each page should identify the appropriate versions, including itself, with destinations that serve the intended translation. Keep the canonical policy for each version consistent with the content you want represented.
The astro-seo component supports languageAlternates, but your application still supplies the real URL mapping. A list of locales is insufficient when translations use different slugs or only some articles have equivalents. Follow Google's localized-page guidance rather than inventing missing siblings to complete an array.
Check one complete group and one untranslated page. The group should have valid reciprocal destinations; the untranslated page should not advertise nonexistent translations. Inspect the visible language and the document's lang value too. Correct annotations cannot compensate for a route that returns the original language or redirects every translated article to a generic landing page.
20. Validate the content contract before rendering
Make essential publishing fields explicit instead of discovering missing values after a layout has rendered a generic fallback. Astro's content collections support schemas that validate the shape of entries.
For an illustrative local posts collection, this configuration requires useful text fields and a publication date:
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
const posts = defineCollection({
loader: glob({ base: './src/content/posts', pattern: '**/*.{md,mdx}' }),
schema: z.object({
title: z.string().trim().min(1),
description: z.string().trim().min(1),
draft: z.boolean().default(false),
publishedAt: z.coerce.date(),
}),
});
export const collections = { posts };
Merge the example with existing collections and use your real directory. Current Astro documentation imports Zod from astro/zod; check the import supported by your locked version before copying it into an older project.
Validation and publication eligibility are separate. A well-formed future-dated post must still be excluded by the rule in check 5. Likewise, a valid description field is useless if the layout never passes it to the head component. Check both the ingestion contract and the rendered output, including an intentionally incomplete record that should fail validation.
21. Compare direct loads with refreshed navigation
Finish on the ordinary public URL. Fetch its headers and complete body, then compare them with the intended release. On Windows, an illustrative request that saves the final body and redirect-chain headers is:
curl.exe -sS -L -D page.headers.txt -o page.html https://example.com/docs/guides/setup/
Inspect each response in the chain, the final status and any robots header. An HTTP 200 alone does not prove that the expected guide, metadata and code examples are present.
If the site uses Astro's ClientRouter, compare a direct load with navigation from another page. Check the title, canonical, body and interactive behavior after the transition. Scripts do not all re-execute identically; a working first load does not establish that navigation lifecycle handling is correct.
For a new or edited CMS record, verify when the build-time collection, live loader and response cache refresh. “Rendered on demand” does not mean every data source is fresh on every request. Record the release identity and observation time with a worksheet such as this:
check,evidence,result
1,Head element owners and installed version,
2,Actual titles and H1 on representative routes,
3,Rendered page-specific descriptions,
4,Initial HTML main content and island boundaries,
5,Published draft and future route outcomes,
6,Preferred URL canonical and final destination,
7,Page-two content canonical and next links,
8,Robots meta and response header policy,
9,Origin robots file and applicable path rules,
10,Expected versus public sitemap URLs,
11,Working base-path internal anchors,
12,Old URL status chain and final replacement,
13,Missing URL error plus known-good page,
14,Social image response and visual review,
15,Body image alt dimensions and asset response,
16,Hydration decisions and measured loading evidence,
17,Parsed safely serialized truthful JSON-LD,
18,Visible byline dates and matching metadata,
19,Real reciprocal language alternates if applicable,
20,Content schema and invalid-record rejection,
21,Direct load transition and fresh release comparison,
Replace the blank results with observations rather than marking the template itself passed. After the public page checks succeed, use Search Console URL Inspection to investigate Google's view when needed. An accepted Request indexing submission is a request outcome, not proof of indexing or rankings. Preserve that distinction alongside the evidence of what you actually published.