Skip to content

Development notes

How static HTML, canonical URLs, hreflang, and sitemaps work together

How page content, canonical URLs, language alternatives, and sitemaps share one public routing contract, based on the recorded static output and verification scripts.

林予晴
Published
  • Astro
  • Static websites
  • SEO basics
  • Route verification

This translation describes the implementation and checks recorded in the original article on October 2, 2026. References to the site’s implementation refer to that record, not a new verification of the current deployment. Unprefixed route examples below are Traditional Chinese routes.

A static website is not finished just because it produces HTML. For a site with three languages, compatibility routes for old URLs, and Markdown notes, at least four related questions remain: does the HTML contain the content for this page, which URL is canonical, do the other language versions actually exist, and which URLs belong in the sitemap?

Maintaining these four things separately can easily produce contradictions. A page looks English, but its canonical URL points to Traditional Chinese. The language switcher offers an article translation that does not exist. The sitemap lists a draft, but no public route serves it. The central judgment is this: canonical URLs, hreflang, sitemaps, and static HTML must all derive from the same actual routing and publication data, rather than being maintained as four independent pieces of copy.

First, confirm that the HTML contains the page content

The site described here uses Astro’s static output. Page components run at build time, and the homepages, fixed pages, notes lists, and published articles produce HTML directly. This differs from a browser shell with only a #app mount point: readers should receive the title, main content, and page information in the initial document.

Output checks verify several basic conditions on each page:

  • The HTML has the correct lang.
  • The page has exactly one main h1.
  • The main content region can be located.
  • The page has a description, an Open Graph title, and an Open Graph description.
  • The body content does not depend on a hydrated framework to appear.
  • Referenced local assets actually exist.

These conditions establish that the document returned by a URL is a readable page. Only then does it make sense to discuss how search engines or sharing services should understand it. A canonical URL cannot rescue HTML with no body content, and a sitemap cannot replace the page itself.

Canonical URLs appear only on indexable pages

The shared layout decides whether to output a canonical URL, language alternatives, og:url, and structured data based on whether the page is indexable. Ordinary public pages use the URL for their own language. The old /work route instead points its canonical URL to the primary route, /about/work.

An easily missed boundary matters here: not every page that returns HTML should declare itself a page for public indexing.

  • Public fixed pages and published articles output a canonical URL.
  • Dev-only article previews output noindex, nofollow and do not declare a public URL.
  • The 404 page outputs noindex and does not canonicalize itself to the homepage.
  • Old compatibility routes retain readable entry points but point their canonical URLs to the primary routes.

If a preview also outputs a public canonical URL, a proofreading URL looks like published content. If a 404 canonicalizes to the homepage, an unknown URL may be misrepresented as another entry point to the homepage. These are problems with page identity, not SEO copy.

hreflang should describe alternatives that exist

Language alternatives for fixed pages can be generated from the set of languages and the fixed-page routes. Traditional Chinese /about, English /en/about, and Japanese /ja/about all have actual pages, so they can list one another as alternatives.

Article pages are different. The translations passed to the article template are the existing translations of the same content. Alternative links come from that set. If an English article does not exist, the Chinese article’s head should not include a guessed /en/notes/... URL.

This distinction defines the responsibility of hreflang: it is neither a list of languages nor a list of pages that might exist in the future. It lists actual alternative URLs readers can visit. Multilingual fixed pages can list the full set; multilingual articles must use their content data.

The sitemap lists only public output

The sitemap does not simply scan every file in src/content/notes/ and list it. It first obtains articles that meet the publication conditions, then includes:

  • Fixed pages in each language.
  • Published note article pages.
  • Paginated notes URLs when there is enough content.

Drafts, articles without publishedAt, articles whose publication dates have not arrived, and nonexistent translations should not appear in the sitemap. The old /work compatibility entry point is also excluded: its job is to keep existing links working, not to become a new primary URL for indexing.

In the recorded implementation, fixed-page URLs and article URLs are generated differently, but their eligibility for public output must remain consistent. Only pages that the production build actually generates have a reason to appear in the sitemap.

What the verification scripts check

This project separates verification into different scopes rather than relying on a single build:

Verification scope What it confirms
Static output checks Fixed pages, compatibility routes, the 404 page, HTML content, metadata, language markers, and local assets
Notes output checks Published article pages, canonical URLs, the sitemap, and the absence of leaked drafts or unpublished body content
Content Collection tests Data contracts for dates, slugs, stable IDs, publication filtering, and dev-only previews
build Whether the code can generate the expected static routes and assets

These checks cannot replace one another. A successful build does not automatically prove that the sitemap contains no drafts. A correct sitemap does not automatically prove that the content order feels comfortable on a phone. Each check should support only the conclusions within its actual scope.

The 404 page and previews need different heads

Published content, previews, and error pages all contain HTML, but they have different public identities. That is why the layout handles notFound, noindex, and public-page metadata separately.

An article preview must let the author see the title, body, and authorship date, without being treated as published content by production output, the sitemap, or search indexes. A 404 page must provide a readable error message and navigation for recovery, but should not provide the homepage’s canonical URL, Open Graph URL, or structured data.

These branches do not exist to make the head more complicated. They separate what readers can see from what the site declares to be an official public page. A readable preview is not necessarily public content. An error page with navigation is not a normal route.

What this contract can and cannot prove

The implementation and static checks recorded in the original article support these conclusions:

  • Public fixed pages and published articles generate readable HTML.
  • Canonical URLs follow the primary routes, with old compatibility URLs pointing to the new primary routes.
  • Fixed pages and existing article translations draw their alternative links from different sources.
  • The sitemap is built only from fixed pages and the published content set.
  • Draft previews and the 404 page do not reuse public pages’ indexing metadata.

This evidence does not directly establish search rankings, indexing speed, social sharing performance, or how every crawler will process the pages. It proves that the output contract holds, not that an external platform has delivered a particular result.

A checking order for static multilingual sites

  1. Confirm that every public URL actually produces the corresponding HTML.
  2. Decide which URL is primary and what the compatibility URLs are responsible for.
  3. Output hreflang only for alternative pages that exist.
  4. Have the sitemap read the same public content set as the public pages.
  5. Keep the noindex rules for previews and the 404 page separate.
  6. Read back the HTML, sitemap, and assets through output checks rather than inspecting only source components.
  7. Report machine checks, browser interactions, and the site owner’s subjective acceptance separately.

Static output offers more than avoiding page assembly on the server at request time. It also requires every public URL, page’s metadata, and content state to agree at build time. If any layer still relies on guesses, a site may appear to have many pages while their public identities remain inconsistent.

Scope of this record

This article documents the fixed pages, note articles, previews, 404 page, canonical URLs, hreflang, sitemap implementation, and output checks recorded in the original article. It does not claim that search rankings, indexing results, sharing-platform fetches, or readers’ understanding have been measured.