Skip to content
Blume
Esc
↑↓navigate↵open⌘Jpreview
On this page

Audit

blume audit crawls the built site and reports SEO and site-health issues — every finding names the source file and front matter line that fixes it.

blume validate reads your content; blume audit reads the built site. It crawls the HTML in dist/ after a build and reports SEO and site-health issues — titles, meta descriptions, canonicals, Open Graph and X cards, headings, hreflang, images, the sitemap, robots.txt, and structured data.

Because Blume built the site, every finding names the source file and the front matter line that fixes it, not just the URL a crawler would see:

⚠ Meta description too long or too short   5 pages
    /docs/configuration/export    content/docs/configuration/export.mdx:3
    fix: Rewrite `description` in the frontmatter to fit the length range.

Run it after a build:

blume build
blume audit

Findings are grouped by check rather than listed per page, so the report reads as a to-do list. Use --verbose to expand every affected page with its full detail, and --only/--skip to work through one category at a time. blume audit --list-checks prints the full catalog, which is also the check catalog at the end of this page — every finding’s docsUrl links to its entry there.

Flags

  • --fail-on error|warning|info — the CI gate: exit non-zero at this severity or above. Defaults to error; --strict is an alias for --fail-on warning.
  • --url <origin> — also probe a live deployment for status codes, response headers, and configured redirects.
  • --external — probe outbound links over the network.
  • --ignore <glob> — with --external, don’t probe an outbound link whose URL matches the glob, like --ignore "https://api.acme.example/**". Repeat the flag for more patterns. It matches the way blume validate --ignore does, and internal links are always checked.
  • --only <check|category> / --skip <check|category> — narrow the report to, or away from, these checks or categories (comma-separated). The audit count in the summary counts only the checks left in. A term that names no check or category is an error that suggests the closest one, so a typo can’t quietly empty the report.
  • --list-checks — print every check the audit can report, then exit.
  • --verbose — list every affected page with each finding’s full detail, instead of the first few.
  • --json — emit the report as JSON on stdout.
  • --codex / --claude — hand the findings to Codex or Claude Code to fix interactively.

Failing CI

The exit code is the contract. By default blume audit fails only on errors — things that are definitely broken, like a link to a page that was never built, a redirect loop, or an invalid sitemap. Advisory findings (a short description, a duplicate title) are warnings and do not fail the build:

blume audit                      # fails on errors
blume audit --fail-on warning    # also fails on warnings

Checking a live deployment

Some things only the real server can tell you: whether a page that exists in dist/ actually 404s behind a bad rewrite, whether responses are compressed, whether an X-Robots-Tag header is quietly deindexing a page whose HTML looks perfectly fine, and whether each old URL in redirects really redirects where you configured it. Point the audit at a deployment to add those checks:

blume audit --url https://docs.example.com
blume audit --url https://docs.example.com --external   # also probe outbound links

These requests go out as a plain HTTP client, not as a crawler: the audit doesn’t send Googlebot’s or an AI crawler’s user agent, since a host’s bot protection can block a client pretending to be one. A firewall or CDN rule that treats crawlers differently isn’t exercised, so check it with the crawler’s own tools, such as Google Search Console’s URL Inspection.

The audit requests every exact redirect’s old URL (a pattern names no single URL to request). One that 404s is an error. One that answers 200 is a warning: a host that doesn’t read the redirect file the build wrote serves the build’s meta-refresh page there instead of an HTTP redirect, which readers follow but crawlers may not. See Redirects for what each host reads.

Outbound links are graded rather than flatly failed: a 404 or 410, or a host name that doesn’t resolve, is a broken link you can fix, while any other error status (a 401 or 403 from a site that turns bots away, a 429, a 5xx), a refused connection, or a timeout is usually rate limiting or someone else’s outage and is reported as a warning. Leave out URLs the audit can’t check from where it runs, like a placeholder domain or a local server, with --ignore.

Fixing the findings with an agent

If you use Codex or Claude Code, the audit can hand its findings straight to it:

blume audit --codex   # or --claude

This writes the complete JSON report — every affected page, not the terminal’s three-page preview — to a file and opens the agent interactively with a prompt that walks it through the findings: edit the source file each finding names, apply its suggested fix, then run blume build and blume audit again until the report is clean. The session is interactive by design: you review the edits through the agent’s own permission flow, and the agent is told never to fix a finding by deleting content.

--only and --skip narrow the handoff the same way they narrow the report, so you can send one category at a time.

What it does and doesn’t check

The check set is deliberately narrower than a general-purpose SEO crawler’s. Much of what such a crawler reports cannot happen to a Blume site — it never emits rel=nofollow, and Vite’s content-hashed bundles are never missing or redirecting — and reporting those as permanent zeroes would just teach you to ignore the report.

Limits worth stating plainly:

  • Structured data is validated for well-formedness (valid JSON, a @context, a @type on every node). Blume does not validate against the full schema.org vocabulary or Google’s rich-results rules.
  • Contrast is checked for the colors your theme config sets, not element by element. The audit doesn’t render pages, so a color you write by hand in MDX or theme.css isn’t measured.
  • Core Web Vitals are not checked. They need a real browser, and a flag that quietly measured nothing would be worse than not having one — so blume audit reports the layout-shift causes it can see offline (images with no width/height, oversized assets) and leaves the rest alone for now.

Anything the audit did not run is reported as skipped rather than silently passing:

⊘ network      skipped — pass --url <origin> (12 checks)
⊘ external     skipped — pass --external (2 checks)

Check catalog

Every check blume audit can report, grouped by category, with its default severity, the tier it runs in, and the fix the report suggests. --only and --skip accept these ids (without the BLUME_AUDIT_ prefix is fine too) or a category’s key. The key isn’t always the category’s heading, so use this table: blume audit --only i18n, not --only Internationalization.

Category Key
Accessibility accessibility
Content content
Duplicates duplicates
Indexability indexability
Links links
Redirects redirects
Social cards social
Internationalization i18n
Assets assets
Sitemap sitemap
robots.txt robots
AI discoverability ai
Structured data structured-data
Live deployment network

Content

Title tag missing or empty

BLUME_AUDIT_TITLE_MISSING · error · from the built HTML

Fix: Add a title to the page’s frontmatter.

Multiple title tags

BLUME_AUDIT_TITLE_MULTIPLE · error · from the built HTML

Fix: Remove the extra <title> from the page’s layout or MDX.

Title too long or too short

BLUME_AUDIT_TITLE_LENGTH · warning · from the built HTML

Fix: Rewrite title in the frontmatter to fit the length range.

Meta description missing or empty

BLUME_AUDIT_DESCRIPTION_MISSING · warning · from the built HTML

Fix: Add a description to the page’s frontmatter.

Multiple meta description tags

BLUME_AUDIT_DESCRIPTION_MULTIPLE · error · from the built HTML

Fix: Remove the extra description <meta> from the page’s layout or MDX.

Meta description too long or too short

BLUME_AUDIT_DESCRIPTION_LENGTH · warning · from the built HTML

Fix: Rewrite description in the frontmatter to fit the length range.

H1 tag missing or empty

BLUME_AUDIT_H1_MISSING · warning · from the built HTML

Fix: Give the page a title — Blume renders it as the page’s <h1>.

Multiple H1 tags

BLUME_AUDIT_H1_MULTIPLE · warning · from the built HTML

Fix: Demote the extra # Heading in the body to ## — Blume already renders title as the h1.

Low word count

BLUME_AUDIT_LOW_WORD_COUNT · info · from the built HTML

Fix: Expand the page, or fold it into a larger one.

Heading levels skip (e.g. h2 to h4)

BLUME_AUDIT_HEADING_SKIP · info · from the built HTML

Fix: Adjust the heading to the next level down — skipped levels break table-of-contents nesting and screen-reader outlines.

Page is dated in the future

BLUME_AUDIT_FUTURE_DATED_PAGE · info · from the built HTML

Fix: Correct the date, or hold the page back until it is meant to be live.

Viewport not set

BLUME_AUDIT_VIEWPORT_MISSING · error · from the built HTML

Fix: Restore the viewport <meta> in your ejected layout.

Duplicates

Duplicate title

BLUME_AUDIT_DUPLICATE_TITLE · warning · from the built HTML

Fix: Give each page a distinct title — search engines show it as the result headline.

Duplicate meta description

BLUME_AUDIT_DUPLICATE_DESCRIPTION · warning · from the built HTML

Fix: Give each page a distinct description.

Duplicate pages without canonical

BLUME_AUDIT_DUPLICATE_CONTENT · warning · from the built HTML

Fix: Merge the pages, or point seo.canonical on all but one at the page to index.

Indexability

deployment.site is not set

BLUME_AUDIT_SITE_NOT_SET · warning · from the built HTML

Fix: Set deployment.site in blume.config.ts to the site’s public URL.

deployment.site is inferred at deploy time

BLUME_AUDIT_SITE_INFERRED_AT_DEPLOY · info · from the built HTML

Fix: Audit a production-like build (e.g. VERCEL=1 VERCEL_PROJECT_PRODUCTION_URL=<host> blume build) or the deployment itself with --url <origin>. Do not hardcode deployment.site — the platform sets it on every deploy.

Page is noindex but declares a canonical

BLUME_AUDIT_CANONICAL_ON_NOINDEX · warning · from the built HTML

Fix: Drop the canonical from noindex pages — Google treats the pairing as contradictory and may ignore one of the two.

Draft page is in the build

BLUME_AUDIT_DRAFT_PAGE_PUBLISHED · warning · from the built HTML

Fix: Rebuild without --preview before deploying, or remove draft: true if the page is ready to ship.

Canonical URL missing

BLUME_AUDIT_CANONICAL_MISSING · warning · from the built HTML

Fix: Set deployment.site so Blume can emit absolute canonical URLs.

Non-canonical page

BLUME_AUDIT_CANONICAL_NOT_SELF · info · from the built HTML

Fix: Point seo.canonical at this page, or remove it to use the default self-canonical.

Canonical points to a broken or redirecting page

BLUME_AUDIT_CANONICAL_BAD_TARGET · error · from the built HTML

Fix: Point seo.canonical at a page that exists and doesn’t redirect.

Canonical protocol does not match the site

BLUME_AUDIT_CANONICAL_PROTOCOL_MISMATCH · error · from the built HTML

Fix: Use the same protocol in seo.canonical as in deployment.site.

Page is not indexable

BLUME_AUDIT_ROBOTS_META_UNEXPECTED · info · from the built HTML

Fix: Remove noindex from the page’s frontmatter if it should be indexed.

Page exceeds Googlebot’s 2 MB crawl limit

BLUME_AUDIT_HTML_TOO_LARGE · error · from the built HTML

Fix: Split the page — Googlebot stops reading an HTML document at 2 MB.

X-Robots-Tag header conflicts with the page’s robots meta

BLUME_AUDIT_ROBOTS_HEADER_CONFLICT · error · needs --url

Fix: Remove the X-Robots-Tag header, or align it with the page’s robots meta.

BLUME_AUDIT_LINK_TO_BROKEN · error · from the built HTML

Fix: Fix the link target, or create the page it points at.

Under deployment.base, a root-relative link without the base is reported here, since the host serves nothing of this build there. A full URL on the same host outside the base isn’t: that’s how a page links to another app the host serves beside the docs.

BLUME_AUDIT_LINK_TO_REDIRECT · warning · from the built HTML

Fix: Link straight to the destination instead of through the redirect.

Orphan page (only reachable from navigation)

BLUME_AUDIT_ORPHAN_PAGE · warning · from the built HTML

Fix: Link to this page from the body of a related page.

BLUME_AUDIT_INTERNAL_LINK_ABSOLUTE · warning · from the built HTML

Fix: Use a root-relative path (/docs/x) so the link survives previews and basePath.

BLUME_AUDIT_INTERNAL_LINK_NOFOLLOW · info · from the built HTML

Fix: Drop rel="nofollow" — it stops this page passing authority internally.

Double slash in URL

BLUME_AUDIT_DOUBLE_SLASH_URL · error · from the built HTML

Fix: Check basePath / deployment.base — a trailing slash there produces //.

Link fragment matches no id on the target page

BLUME_AUDIT_ANCHOR_BROKEN · warning · from the built HTML

Fix: Point the fragment at a heading that exists on the target page, or fix the heading it meant.

URL contains uppercase, underscores, or spaces

BLUME_AUDIT_URL_STYLE · info · from the built HTML

Fix: Rename the source file to a lowercase, hyphenated slug — and add a redirect from the old URL if it was already published.

Redirects

Broken redirect

BLUME_AUDIT_REDIRECT_BROKEN · error · from the built HTML

Fix: Point the redirect at a page that exists.

Redirect loop

BLUME_AUDIT_REDIRECT_LOOP · error · from the built HTML

Fix: Break the cycle in redirects — it never resolves.

Redirect chain

BLUME_AUDIT_REDIRECT_CHAIN · warning · from the built HTML

Fix: Point every hop straight at the final destination.

Meta refresh redirect

BLUME_AUDIT_META_REFRESH · warning · from the built HTML

Fix: Use a real redirect in blume.config.ts instead of a meta refresh.

Redirect source is also a real page

BLUME_AUDIT_REDIRECT_SOURCE_IS_PAGE · error · from the built HTML

Fix: Remove the redirect, or delete the page it shadows — a path can’t be both, and which one readers get depends on the host.

HTTPS to HTTP redirect

BLUME_AUDIT_REDIRECT_TO_HTTP · error · needs --url

Fix: Redirect to the HTTPS URL.

Live site doesn’t redirect as configured

BLUME_AUDIT_REDIRECT_NOT_SERVED · error · needs --url

Fix: Deploy the redirect file your host reads (see Redirects in the deployment docs), or name the host in deployment.

A warning instead when the old URL answers 200 (the meta-refresh page) or redirects somewhere other than the configured destination.

Social cards

Open Graph tags missing or incomplete

BLUME_AUDIT_OG_INCOMPLETE · warning · from the built HTML

Fix: Add a description — Blume fills the rest of the Open Graph tags for you.

Open Graph image missing

BLUME_AUDIT_OG_IMAGE_MISSING · warning · from the built HTML

Fix: Set deployment.site to turn on generated OG images, or set seo.image on the page.

Open Graph image is not in the build

BLUME_AUDIT_OG_IMAGE_BROKEN · warning · from the built HTML

Fix: Point seo.image at a file that exists, or rebuild — a dead og:image renders as a blank card everywhere the page is shared.

Open Graph image is too small for large cards

BLUME_AUDIT_OG_IMAGE_SMALL · warning · from the built HTML

Fix: Use an image of at least 1200×630 — smaller ones render blurry or get cropped into small-card layouts.

Open Graph URL not matching canonical

BLUME_AUDIT_OG_URL_MISMATCH · warning · from the built HTML

Fix: Align og:url with the page’s canonical URL.

X (Twitter) card missing or incomplete

BLUME_AUDIT_TWITTER_CARD_INCOMPLETE · warning · from the built HTML

Fix: Set seo.x.handle in blume.config.ts so X can attribute the card.

Internationalization

HTML lang attribute missing

BLUME_AUDIT_HTML_LANG_MISSING · error · from the built HTML

Fix: Restore the lang attribute on <html> in your ejected layout.

HTML lang attribute invalid

BLUME_AUDIT_HTML_LANG_INVALID · error · from the built HTML

Fix: Use a valid BCP 47 tag (e.g. en, en-GB) for the locale.

Hreflang and HTML lang mismatch

BLUME_AUDIT_HREFLANG_LANG_MISMATCH · error · from the built HTML

Fix: The page’s <html lang> must match its own hreflang annotation.

Hreflang annotation invalid

BLUME_AUDIT_HREFLANG_INVALID · error · from the built HTML

Fix: Use a valid BCP 47 tag in the hreflang annotation.

Self-reference hreflang annotation missing

BLUME_AUDIT_HREFLANG_SELF_MISSING · warning · from the built HTML

Fix: A page’s hreflang set must include a self-reference.

X-default hreflang annotation missing

BLUME_AUDIT_HREFLANG_XDEFAULT_MISSING · info · from the built HTML

Fix: Add an x-default alternate pointing at the default-locale page.

Missing reciprocal hreflang (no return-tag)

BLUME_AUDIT_HREFLANG_NO_RETURN_TAG · error · from the built HTML

Fix: Every page in an hreflang group must link back to every other one.

Hreflang points to a broken, redirecting, or non-canonical page

BLUME_AUDIT_HREFLANG_BAD_TARGET · error · from the built HTML

Fix: Point the hreflang alternate at a page that exists and is canonical.

Hreflang group has a language conflict

BLUME_AUDIT_HREFLANG_CONFLICT · error · from the built HTML

Fix: Each language in an hreflang group must name exactly one page.

Accessibility

These run on your theme config, not on each page: a theme color is one choice for the whole site. The check resolves colors the way the build does (a preset’s shade for each mode, and white or dark labels on fills) and measures every text color the theme draws against the WCAG AA ratio for body text, 4.5:1, in light and dark mode.

Theme colors miss WCAG AA contrast

BLUME_AUDIT_THEME_CONTRAST_LOW · warning · from the theme config

Fix: Pick a darker shade for light mode or a lighter one for dark mode, per mode with theme.accent: { light, dark }.

It checks accent text on the page, labels on accent and action fills, and body and secondary text on a custom background. Each finding names the pair, the mode, and its ratio.

Theme color can’t be checked for contrast

BLUME_AUDIT_THEME_COLOR_UNCHECKED · info · from the theme config

Fix: Write the color as hex, rgb(), hsl(), hwb(), lab(), lch(), oklab(), oklch(), or a named color so its contrast can be checked.

Assets

Missing alt text

BLUME_AUDIT_IMAGE_ALT_MISSING · warning · from the built HTML

Fix: Add descriptive alt text, or alt="" if the image is decorative.

Image broken

BLUME_AUDIT_IMAGE_BROKEN · error · from the built HTML

Fix: Fix the image path, or add the file to public/.

Asset file size too large

BLUME_AUDIT_ASSET_TOO_LARGE · warning · from the built HTML

Fix: Compress the asset, or serve a modern format (WebP/AVIF).

Image has no width/height

BLUME_AUDIT_IMAGE_MISSING_DIMENSIONS · warning · from the built HTML

Fix: Set width and height so the browser can reserve space (avoids layout shift).

Referenced script, style, or asset is missing from the build

BLUME_AUDIT_SUBRESOURCE_MISSING · error · from the built HTML

Fix: Fix the reference, or restore the missing file.

HTTPS/HTTP mixed content

BLUME_AUDIT_MIXED_CONTENT · error · from the built HTML

Fix: Load the subresource over HTTPS — browsers block mixed content.

Sitemap

Indexable page not in sitemap

BLUME_AUDIT_INDEXABLE_PAGE_NOT_IN_SITEMAP · warning · from the built HTML

Fix: Remove draft/hidden/noindex from the page’s frontmatter if it should be indexed. A public/sitemap.xml replaces the generated sitemap, so add the page there or delete that file.

Noindex page in sitemap

BLUME_AUDIT_NOINDEX_IN_SITEMAP · error · from the built HTML

Fix: A noindex page should not be advertised in the sitemap.

Non-canonical page in sitemap

BLUME_AUDIT_NON_CANONICAL_IN_SITEMAP · error · from the built HTML

Fix: List only canonical URLs in the sitemap.

Sitemap names a page that does not exist or redirects

BLUME_AUDIT_SITEMAP_BAD_URL · error · from the built HTML

Fix: Remove the URL from the sitemap, or build the page it names.

Sitemap has a syntax error or wrong format

BLUME_AUDIT_SITEMAP_INVALID · error · from the built HTML

Fix: Sitemaps must be valid XML in the sitemaps.org urlset or sitemap index format.

Sitemap exceeds 50 MB or 50,000 URLs

BLUME_AUDIT_SITEMAP_TOO_LARGE · error · from the built HTML

Fix: Split the sitemap — the limits are 50 MB and 50,000 URLs.

Sitemap lastmod is invalid or in the future

BLUME_AUDIT_SITEMAP_LASTMOD_INVALID · warning · from the built HTML

Fix: Use a real W3C date that is not in the future — search engines that catch a sitemap lying about freshness stop trusting its lastmod entirely.

Sitemap includes URLs out of its scope

BLUME_AUDIT_SITEMAP_OUT_OF_SCOPE · warning · from the built HTML

Fix: A sitemap may only list URLs on its own origin.

Sitemap is not accessible

BLUME_AUDIT_SITEMAP_NOT_ACCESSIBLE · error · needs --url

Fix: Make sitemap.xml reachable at the site root.

robots.txt

robots.txt missing

BLUME_AUDIT_ROBOTS_MISSING · warning · from the built HTML

Fix: Set seo.robots: true to generate robots.txt.

robots.txt has a syntax error

BLUME_AUDIT_ROBOTS_INVALID · error · from the built HTML

Fix: Every robots.txt line must be a Field: value directive or a comment.

robots.txt disallows a page that is in the sitemap

BLUME_AUDIT_ROBOTS_DISALLOWS_INDEXABLE · error · from the built HTML

Fix: A page can’t be both disallowed in robots.txt and advertised in the sitemap.

robots.txt blocks one crawler from pages in the sitemap

BLUME_AUDIT_ROBOTS_BLOCKS_CRAWLER · warning · from the built HTML

Fix: Narrow or remove the rule in that crawler’s User-agent group if it should read these pages. Blocking an AI crawler on purpose is fine; skip this check.

A crawler named in its own User-agent group follows only that group, so a rule there (User-agent: GPTBot then Disallow: /) shuts it out while every other crawler reads the page. The audit reports each crawler once, at the rule that blocks its first page.

robots.txt does not reference the sitemap

BLUME_AUDIT_ROBOTS_SITEMAP_MISSING · info · from the built HTML

Fix: Set deployment.site so robots.txt can reference the sitemap.

robots.txt is not accessible

BLUME_AUDIT_ROBOTS_NOT_ACCESSIBLE · error · needs --url

Fix: Make robots.txt reachable at the site root.

AI discoverability

llms.txt missing from the build

BLUME_AUDIT_LLMS_TXT_MISSING · warning · from the built HTML

Fix: Rebuild — agents.llmsTxt is enabled but the build has no llms.txt. If that’s intentional, set agents.llmsTxt: false.

llms.txt lists a page the build does not serve

BLUME_AUDIT_LLMS_TXT_STALE_ENTRY · warning · from the built HTML

Fix: Rebuild so llms.txt matches the site — a stale entry sends an AI agent to a page that is not there.

Indexable page missing from llms.txt

BLUME_AUDIT_LLMS_TXT_PAGE_MISSING · warning · from the built HTML

Fix: Rebuild so llms.txt matches the site; if the page is deliberately excluded, set ai.exclude: true in its front matter.

No DNS-AID agent-discovery records

BLUME_AUDIT_DNS_AID_MISSING · info · needs --url

Fix: Publish a ServiceMode SVCB or HTTPS record at _index._agents.<host> with your DNS provider so agents can discover the site via DNS (DNS-AID).

DNS-AID records are not DNSSEC-authenticated

BLUME_AUDIT_DNS_AID_UNSIGNED · info · needs --url

Fix: Enable DNSSEC for the zone so validating resolvers return authenticated DNS-AID answers; if your DNS provider doesn’t support DNSSEC, the records still work unsigned.

Structured data

Structured data is not valid JSON

BLUME_AUDIT_JSONLD_INVALID · error · from the built HTML

Fix: The JSON-LD block must be valid JSON.

Structured data is missing required properties

BLUME_AUDIT_JSONLD_INCOMPLETE · warning · from the built HTML

Fix: Every JSON-LD node needs @context and @type.

Live deployment

4XX page

BLUME_AUDIT_HTTP_4XX · error · needs --url

Fix: The page is linked or in the sitemap but the deployment 404s it.

5XX page

BLUME_AUDIT_HTTP_5XX · error · needs --url

Fix: The deployment is erroring on this page.

Timed out

BLUME_AUDIT_HTTP_TIMEOUT · error · needs --url

Fix: The page did not respond in time.

Not compressed

BLUME_AUDIT_NOT_COMPRESSED · warning · needs --url

Fix: Enable gzip or brotli on the host.

Slow page

BLUME_AUDIT_SLOW_RESPONSE · warning · needs --url

Fix: The page was slow to respond.

BLUME_AUDIT_EXTERNAL_LINK_BROKEN · error · needs --external

Fix: Fix or remove the outbound link.

BLUME_AUDIT_EXTERNAL_LINK_REDIRECT · info · needs --external

Fix: Link straight to the destination.

Last updated on

Was this page helpful?