216 rules in 18 categories

The rules of Agentic SEO Check.

What each rule finds, and what to change. Open a rule for its fix.

Titles

5 rules

title.missing

ErrorThe page has no <title> element or it is empty.

What to change: Add a unique, descriptive <title> in <head> (about 30-60 characters) that states what the page is about.

Documentation

title.multiple

WarningMore than one <title> element is present.

What to change: Keep exactly one <title>. Browsers and crawlers may pick different ones when several exist.

Documentation

title.too-long

WarningTitle is longer than the configured maximum (default 60 characters) and will be truncated in search results.

What to change: Shorten the title to roughly 60 characters or fewer, keeping the most important words first.

Documentation

title.too-short

InfoTitle is shorter than the configured minimum (default 30 characters).

What to change: Consider a more descriptive title that includes the page topic and, where sensible, the brand.

Documentation

title.generic

WarningTitle is a framework default or placeholder such as "React App", "Create Next App", "Home" or "Untitled".

What to change: Replace the placeholder with a title that describes this page. In frameworks, set it per route (metadata export, <Head>, useHead, <svelte:head>, etc.).

Documentation

Meta descriptions

5 rules

meta-description.missing

WarningNo <meta name="description"> is present or it is empty.

What to change: Add a unique meta description (about 70-160 characters) that summarises the page and invites the click. Search engines may show it as the snippet.

Documentation

meta-description.multiple

WarningMore than one meta description is present.

What to change: Keep a single meta description; remove or merge the others.

Documentation

meta-description.too-long

WarningMeta description exceeds the configured maximum (default 160 characters) and will be truncated.

What to change: Trim the description to about 160 characters, front-loading the key message.

Documentation

meta-description.too-short

InfoMeta description is shorter than the configured minimum (default 70 characters).

What to change: Expand the description so it summarises the page content and its value to the visitor.

Documentation

meta-description.same-as-title

InfoMeta description is identical to the title.

What to change: Write a description that adds information beyond the title instead of repeating it.

Documentation

Headings

6 rules

h1.missing

WarningThe page has no <h1>.

What to change: Add one <h1> that states the main topic of the page. It is usually the visible page heading.

h1.multiple

InfoThe page has more than one <h1>.

What to change: Prefer a single <h1> for the main topic and use <h2>-<h6> for sections. Multiple H1s are valid HTML but make the main topic less clear.

h1.empty

WarningAn <h1> exists but contains no text.

What to change: Put the page heading text inside the <h1>. If it wraps only an image, give the image descriptive alt text.

h1.too-long

InfoThe <h1> is longer than the configured maximum (default 70 characters).

What to change: Keep the H1 concise; move detail into the intro paragraph.

headings.empty

WarningHeading elements (h2-h6) with no text content.

What to change: Remove empty heading tags or fill them with text. Empty headings confuse outline parsers and screen readers.

headings.skipped-level

InfoHeading levels are skipped (for example an <h2> followed directly by an <h4>).

What to change: Use heading levels in order (h1 > h2 > h3) so the document outline reflects the content structure.

Canonical URLs

8 rules

canonical.missing

WarningNo <link rel="canonical"> is present.

What to change: Add a self-referencing canonical with the absolute preferred URL (scheme, host, trailing slash) so duplicate URLs (query strings, www/non-www) consolidate to it.

Documentation

canonical.header-conflict

ErrorThe HTTP Link header names a different canonical URL than the page's <link rel="canonical">, or names more than one.

What to change: Name one canonical URL, in one place: the <link rel="canonical"> in <head>, or the Link header (meant for PDFs and other files without HTML). Google asks not to give different canonical URLs through different methods. A CDN, plugin or platform rule often sets the header; remove the one you did not mean.

Documentation

canonical.multiple

ErrorMore than one canonical link is present. When they disagree, search engines ignore all of them.

What to change: Emit exactly one canonical link per page. Check for a framework default plus a hand-written one.

Documentation

canonical.invalid

ErrorThe canonical href is empty or cannot be resolved to an http(s) URL.

What to change: Set the canonical href to a valid absolute URL such as https://www.example.com/path/.

Documentation

canonical.relative

WarningThe canonical href is a relative URL.

What to change: Use an absolute URL in the canonical link. Relative canonicals are error-prone and some crawlers resolve them unexpectedly.

Documentation

canonical.protocol-mismatch

WarningThe page is served over https but the canonical points to http.

What to change: Point the canonical at the https URL.

Documentation

canonical.other-page

InfoThe canonical points to a different URL, so search engines will treat this page as a duplicate of that URL.

What to change: If this page should rank on its own, make the canonical self-referencing. If it is intentionally a duplicate (print view, filtered list, tracking URL), this is correct.

Documentation

canonical.paginated-to-first

WarningPage 2 or later of a paginated series (?page=2, /page/2/) names the first page as its canonical, so search engines may drop it and miss the items only it links to.

What to change: Give each page of the series a canonical that names its own URL (?page=2 names ?page=2), and link the pages to each other with plain <a href> links. Google advises against making the first page the canonical of the series.

Documentation

Indexing and robots

7 rules

robots.noindex

WarningThe page tells search engines not to index it (meta robots or X-Robots-Tag).

What to change: If this page should appear in search results, remove the noindex directive. Preview/staging hosts often add X-Robots-Tag: noindex at the platform level; verify on production. If the page is intentionally private (login, thank-you, internal search), this is correct.

Documentation

robots.nofollow

InfoThe page tells search engines not to follow its links.

What to change: Remove nofollow unless you deliberately want link equity to stop at this page; it also hides linked pages from discovery.

Documentation

robots.conflicting

InfoThe page's robots directives contradict each other (index and noindex, or follow and nofollow); Google applies the more restrictive one.

What to change: Keep one directive for indexing and one for following, in one place. A platform or CDN often adds X-Robots-Tag on top of the page's own meta robots; remove the one you did not mean.

Documentation

robots.malformed-directive

WarningA robots directive is written in a form Google does not document, such as "no-index" for noindex (warning) or "noindex nofollow" without a comma (info).

What to change: Write each directive the way Google documents it, separated by commas: content="noindex, nofollow". A misspelled noindex is ignored, so the page stays indexable.

Documentation

robots.unavailable-after

WarningThe page sets unavailable_after (meta robots or X-Robots-Tag) to a date that has passed, so Google no longer shows it in search results (warning), or to a date Google cannot read, so it ignores the rule (info).

What to change: Remove unavailable_after if the page should show in search again; a template or plugin often leaves an old date in place. To have a page drop out later, write the date in ISO 8601 (2026-12-31), RFC 822 or RFC 850.

Documentation

robots.max-image-preview-missing

InfoAn article page sets no max-image-preview, so Google Discover shows it with a small thumbnail instead of a large image.

What to change: Add max-image-preview:large to the page's robots meta tag (<meta name="robots" content="max-image-preview:large">), and give it an image at least 1200 pixels wide. Google Discover shows large images only to pages that allow them, or to AMP pages.

Documentation

meta-refresh.redirect

WarningA <meta http-equiv="refresh"> sends visitors to another URL. Google reads an instant one as a permanent redirect (info) and a delayed one as a temporary redirect (warning), and prefers a redirect from the server.

What to change: Redirect on the server with a 301 (or 308) to the new URL. If the server cannot redirect, set the meta refresh to 0 seconds, which Google reads as a permanent redirect.

Documentation

The document

12 rules

html.lang-missing

WarningThe <html> element has no lang attribute.

What to change: Set <html lang="xx"> (for example "en", "nl", "en-GB") so search engines and screen readers know the content language.

html.lang-invalid

WarningThe <html lang> value is not a valid BCP 47 language tag.

What to change: Use a BCP 47 tag such as "en", "en-US" or "pt-BR".

viewport.missing

WarningNo <meta name="viewport"> is present, so the page is not mobile-friendly.

What to change: Add <meta name="viewport" content="width=device-width, initial-scale=1"> to <head>. Google indexes the mobile version of pages first.

Documentation

viewport.not-scalable

InfoThe viewport disables user zoom (user-scalable=no or maximum-scale=1).

What to change: Remove user-scalable=no and maximum-scale limits; blocking zoom is an accessibility problem flagged by Lighthouse.

charset.missing

InfoNo character encoding is declared, in the HTML or in the Content-Type header.

What to change: Add <meta charset="utf-8"> as the first element in <head>.

doctype.missing

InfoThe document does not start with <!DOCTYPE html>.

What to change: Start the document with <!DOCTYPE html> to avoid quirks-mode rendering.

document.html-too-large

InfoThe HTML document is larger than 1.5 MB.

What to change: Reduce inline scripts/styles/data (large JSON state, inline SVG sprites). Very large documents slow parsing and may be truncated by crawlers.

head.closed-early

ErrorAn element that does not belong in <head> (an <img>, <iframe>, <div>, stray text) ends it early, so search engines read the metadata after it as part of <body> and ignore it.

What to change: Move the element out of <head>, into <body>, or at least below every <title>, <meta> and <link>. Only <title>, <meta>, <link>, <script>, <style>, <base>, <noscript> and <template> belong in <head>. A tag manager's or ad pixel's <noscript><iframe> or <img> goes right after the opening <body> tag.

Documentation

head.metadata-in-body

WarningMetadata that search engines read only in <head> (title, meta description, viewport, canonical, hreflang) is written in <body> (warning). Metadata a framework streams into a hidden part of <body> and moves into <head> with JavaScript, as Next.js does for an async generateMetadata, is info. Robots meta tags are not reported: Google honours them in <body> too.

What to change: Move these elements into <head>. Google accepts rel=canonical only in <head>. A framework that renders them in a page component usually has a head API for them (metadata export, <Head>, useHead, <svelte:head>).

Documentation

charset.late

InfoThe character encoding is declared after the first 1024 bytes, where the HTML spec requires it, and the Content-Type header does not declare it.

What to change: Put <meta charset="utf-8"> first in <head>, before any script, style or comment, or send the encoding in the Content-Type header (text/html; charset=utf-8).

Documentation

https.mixed-content

WarningAn https page loads resources over http. Browsers block scripts, stylesheets, frames and responsive images (warning) and upgrade plain images, audio and video to https, which fails when the https URL does not exist (info). With a Content-Security-Policy of upgrade-insecure-requests, every resource is upgraded (info).

What to change: Load every resource over https: change http:// URLs to https:// (or make them relative), and host or replace any resource that has no https version. A Content-Security-Policy of upgrade-insecure-requests upgrades the rest.

Documentation

favicon.missing

InfoThe home page has no <link rel="icon"> (or apple-touch-icon). Google takes the icon it shows next to the site in search results from the home page.

What to change: Add <link rel="icon" href="/favicon.svg"> (or a square PNG larger than 48x48 pixels) to the home page's <head>, and keep the file crawlable. Google documents only the link, not a /favicon.ico fallback.

Documentation

Open Graph and Twitter cards

6 rules

og.missing

WarningNo Open Graph tags (og:title, og:description, og:image) are present.

What to change: Add og:title, og:description, og:image (absolute URL, at least 1200x630), og:url and og:type so shares on social platforms and chat apps render a rich preview.

Documentation

og.incomplete

InfoSome Open Graph tags are present but one or more of og:title, og:description, og:image, og:url, og:type is missing.

What to change: Fill in the missing Open Graph properties. og:image and og:description have the biggest effect on share previews.

Documentation

og.image-not-absolute

Warningog:image is not an absolute URL.

What to change: Use a full https:// URL for og:image; most platforms ignore relative image paths.

Documentation

og.url-mismatch

Infoog:url differs from the canonical URL.

What to change: Set og:url to the same URL as the canonical so share counts and previews consolidate.

Documentation

twitter.card-missing

InfoNo twitter:card meta tag is present.

What to change: Add <meta name="twitter:card" content="summary_large_image">; X/Twitter falls back to Open Graph for the other fields.

Documentation

twitter.card-invalid

Warningtwitter:card has a value other than summary, summary_large_image, app or player.

What to change: Use one of the supported card types, usually summary_large_image.

Documentation

Images

7 rules

img.alt-missing

WarningImages without an alt attribute.

What to change: Give every <img> an alt attribute: a short description for meaningful images, alt="" for purely decorative ones. Alt text is used for image search and accessibility.

Documentation

img.alt-filename

InfoImage alt text looks like a filename (IMG_1234.jpg, screenshot.png).

What to change: Replace filename-style alt text with a description of what the image shows.

Documentation

img.dimensions-missing

InfoImages without explicit width and height attributes, which causes layout shift (CLS) while they load.

What to change: Add width and height attributes (or CSS aspect-ratio) to <img> elements so the browser can reserve space before the image loads.

Documentation

img.src-missing

InfoImages without a src attribute (usually lazy-loading via data-src without a fallback).

What to change: Use native loading="lazy" with a real src, or ensure the lazy-loader also works without JavaScript, so crawlers can discover the images.

Documentation

img.unsupported-format

InfoImages in a format Google Images does not index (HEIC, HEIF, TIFF, JPEG XL, JPEG 2000, PSD). Most browsers cannot show them either.

What to change: Convert these images to WebP, AVIF, JPEG or PNG, and point the <img src> at the converted file. Google Images indexes BMP, GIF, JPEG, PNG, WebP, SVG and AVIF.

Documentation

img.lazy-above-fold

InfoThe first large image on the page, likely in the first screen and often the Largest Contentful Paint, has loading="lazy", which delays it (info). An image with both fetchpriority="high" and loading="lazy" contradicts itself (warning).

What to change: Remove loading="lazy" from images in the first screen, and give the main one fetchpriority="high". Keep lazy loading for images further down the page. In Next.js, set priority (or preload) on that <Image>.

Documentation

img.srcset-missing

InfoImages 640 pixels wide or more (by their width attribute) have no srcset or <picture>, so phones download the full-size file.

What to change: Offer smaller versions with srcset and sizes (srcset="hero-480.jpg 480w, hero-960.jpg 960w" sizes="100vw"), or use the framework's image component (next/image, astro:assets, NuxtImg), which generates them.

Documentation

Performance

17 rules

loading.render-blocking-script

InfoScripts in <head> load without async, defer or type="module", so the browser stops building the page until each one has downloaded and run.

What to change: Add defer to scripts the page needs once it is parsed, or async to independent ones such as analytics; type="module" scripts are deferred already. A script that must run before anything renders (a consent manager, an A/B test) can stay, but keep it small.

Documentation

loading.no-compression

WarningThe HTML is sent uncompressed although the request accepted gzip, so every visitor and crawler downloads several times more bytes than needed.

What to change: Turn on gzip or Brotli compression for text/html (and CSS, JavaScript, JSON, SVG) in the web server, CDN or hosting settings: gzip on in nginx, mod_deflate in Apache, compression() middleware in Express.

Documentation

perf.field-lcp-poor

Warning--field-dataOn phones, real visits wait more than 4,000 ms for the largest element of the first screen at the 75th percentile: Largest Contentful Paint (LCP) in the Chrome UX Report's last 28 days, for the page's URL or, when it has too few visits, its origin. Google rates it poor.

What to change: Make the largest element of the first screen show sooner: never lazy-load it, give its image fetchpriority="high", defer the scripts in <head>, and speed up the server's first byte (cache the HTML at the edge). The Chrome UX Report covers 28 days, so a fix shows there over the next four weeks; check it now with --lighthouse on the local tool, or PageSpeed Insights, which also names the element.

Runs with --field-data (fieldData), on the command and both MCP servers, and in this site's audits: it reads the Chrome UX Report's real-user data.

Documentation

perf.field-inp-poor

Warning--field-dataOn phones, real visits wait more than 500 ms for the page to answer a tap or a key at the 75th percentile: Interaction to Next Paint (INP) in the Chrome UX Report's last 28 days, for the page's URL or, when it has too few visits, its origin. Google rates it poor.

What to change: Shorten the work a tap or a key starts: split long JavaScript tasks (await scheduler.yield()), defer scripts in <head>, load other sites' scripts after the page, and keep the viewport meta tag so phones answer a tap at once. The Chrome UX Report covers 28 days, so a fix shows there over the next four weeks; check it now with --lighthouse on the local tool, or PageSpeed Insights, which also names the element.

Runs with --field-data (fieldData), on the command and both MCP servers, and in this site's audits: it reads the Chrome UX Report's real-user data.

Documentation

perf.field-cls-poor

Warning--field-dataOn phones, the layout of real visits shifts by more than 0.25 at the 75th percentile: Cumulative Layout Shift (CLS) in the Chrome UX Report's last 28 days, for the page's URL or, when it has too few visits, its origin. Google rates it poor.

What to change: Give every image width and height, reserve the space of what a script inserts (banners, ads, embeds), and load web fonts with font-display: optional. The Chrome UX Report covers 28 days, so a fix shows there over the next four weeks; check it now with --lighthouse on the local tool, or PageSpeed Insights, which also names the element.

Runs with --field-data (fieldData), on the command and both MCP servers, and in this site's audits: it reads the Chrome UX Report's real-user data.

Documentation

perf.field-lcp-needs-improvement

Info--field-dataOn phones, real visits wait more than 2,500 ms for the largest element of the first screen at the 75th percentile: Largest Contentful Paint (LCP) in the Chrome UX Report's last 28 days, for the page's URL or, when it has too few visits, its origin. Google rates it as needing improvement.

What to change: Make the largest element of the first screen show sooner: never lazy-load it, give its image fetchpriority="high", defer the scripts in <head>, and speed up the server's first byte (cache the HTML at the edge). The Chrome UX Report covers 28 days, so a fix shows there over the next four weeks; check it now with --lighthouse on the local tool, or PageSpeed Insights, which also names the element.

Runs with --field-data (fieldData), on the command and both MCP servers, and in this site's audits: it reads the Chrome UX Report's real-user data.

Documentation

perf.field-inp-needs-improvement

Info--field-dataOn phones, real visits wait more than 200 ms for the page to answer a tap or a key at the 75th percentile: Interaction to Next Paint (INP) in the Chrome UX Report's last 28 days, for the page's URL or, when it has too few visits, its origin. Google rates it as needing improvement.

What to change: Shorten the work a tap or a key starts: split long JavaScript tasks (await scheduler.yield()), defer scripts in <head>, load other sites' scripts after the page, and keep the viewport meta tag so phones answer a tap at once. The Chrome UX Report covers 28 days, so a fix shows there over the next four weeks; check it now with --lighthouse on the local tool, or PageSpeed Insights, which also names the element.

Runs with --field-data (fieldData), on the command and both MCP servers, and in this site's audits: it reads the Chrome UX Report's real-user data.

Documentation

perf.field-cls-needs-improvement

Info--field-dataOn phones, the layout of real visits shifts by more than 0.10 at the 75th percentile: Cumulative Layout Shift (CLS) in the Chrome UX Report's last 28 days, for the page's URL or, when it has too few visits, its origin. Google rates it as needing improvement.

What to change: Give every image width and height, reserve the space of what a script inserts (banners, ads, embeds), and load web fonts with font-display: optional. The Chrome UX Report covers 28 days, so a fix shows there over the next four weeks; check it now with --lighthouse on the local tool, or PageSpeed Insights, which also names the element.

Runs with --field-data (fieldData), on the command and both MCP servers, and in this site's audits: it reads the Chrome UX Report's real-user data.

Documentation

perf.lcp-slow

Warning--lighthouseLargest Contentful Paint above 2.5 s (poor above 4 s). Lab measurement from Lighthouse.

What to change: Preload the LCP image/font, serve it from a CDN in a modern format, inline critical CSS, and cut server response time.

Runs with --lighthouse, on the command or the local server.

Documentation

perf.cls-high

Warning--lighthouseCumulative Layout Shift above 0.1 (poor above 0.25).

What to change: Reserve space for images, ads and embeds with width/height or aspect-ratio; avoid inserting content above existing content; use font-display: optional or size-adjusted fallbacks.

Runs with --lighthouse, on the command or the local server.

Documentation

perf.tbt-high

Warning--lighthouseTotal Blocking Time above 200 ms (poor above 600 ms). Lab proxy for Interaction to Next Paint.

What to change: Split long JavaScript tasks, defer non-critical scripts, remove unused JavaScript, and move work off the main thread.

Runs with --lighthouse, on the command or the local server.

Documentation

perf.fcp-slow

Info--lighthouseFirst Contentful Paint above 1.8 s (poor above 3 s).

What to change: Eliminate render-blocking resources, inline critical CSS, and reduce server response time.

Runs with --lighthouse, on the command or the local server.

Documentation

perf.ttfb-slow

Warning--lighthouseServer response time (TTFB) above 800 ms.

What to change: Cache HTML at the edge, avoid slow data fetches during render, and use a nearby region/CDN.

Runs with --lighthouse, on the command or the local server.

Documentation

perf.score-low

Warning--lighthouseLighthouse performance score below 50.

What to change: Work through the opportunities listed in the performance result, biggest savings first.

Runs with --lighthouse, on the command or the local server.

Documentation

perf.opportunity

Info--lighthouseA Lighthouse opportunity with measurable estimated savings.

What to change: Apply the Lighthouse recommendation described in the finding.

Runs with --lighthouse, on the command or the local server.

Documentation

lighthouse.seo-audit-failed

Warning--lighthouseA Lighthouse SEO audit failed (rendered-DOM checks such as crawlable anchors, robots.txt validity).

What to change: Follow the Lighthouse audit description in the finding.

Runs with --lighthouse, on the command or the local server.

Documentation

lighthouse.agentic-audit-failed

Info--lighthouseA Lighthouse agentic-browsing audit failed (llms.txt, accessibility tree quality, WebMCP). Experimental.

What to change: Follow the Lighthouse audit description. These checks reflect how well AI agents can use the site and are not classic ranking factors.

Runs with --lighthouse, on the command or the local server.

Documentation

hreflang and languages

6 rules

hreflang.invalid-code

Warninghreflang values that are not valid ISO 639-1 language codes with an optional ISO 3166-1 region (or x-default).

What to change: Use codes like "en", "en-gb", "nl-nl", "zh-hant" or "x-default". Country codes alone ("uk", "eu") are invalid.

Documentation

hreflang.relative-url

Warninghreflang links must use absolute URLs.

What to change: Use fully qualified URLs (https://example.com/nl/) in every hreflang link.

Documentation

hreflang.duplicate-lang

WarningThe same hreflang value appears more than once with different URLs.

What to change: Each language/region code may map to exactly one URL per page.

Documentation

hreflang.missing-self

WarningThe hreflang set does not include this page itself (self-referencing entry).

What to change: Every page in an hreflang cluster must list itself as well as its alternates, otherwise the annotations may be ignored.

Documentation

hreflang.missing-x-default

InfoThe hreflang set has no x-default entry.

What to change: Add <link rel="alternate" hreflang="x-default" href="..."> pointing at the language-selector or default version.

Documentation

hreflang.canonical-other-language

WarningThe canonical points to an hreflang alternate in another language, so search engines treat this page as a copy of that one and leave it out of the index.

What to change: Give each language version a canonical that names its own URL, and let hreflang link the versions. Google asks for a canonical in the same language as the page.

Documentation

Structured data

14 rules

structured-data.invalid-json

ErrorA JSON-LD <script> block contains invalid JSON and will be ignored entirely.

What to change: Fix the JSON syntax (trailing commas, unescaped quotes, HTML comments inside the script). Serialise with JSON.stringify instead of hand-writing it.

Documentation

structured-data.missing

InfoThe page has no structured data (JSON-LD or microdata).

What to change: Add JSON-LD for the page type (Article, Product, Organization, BreadcrumbList, Event, Recipe, ...) to qualify for rich results.

Documentation

structured-data.missing-context

WarningA JSON-LD item has no @context or a context other than schema.org.

What to change: Set "@context": "https://schema.org" on every top-level JSON-LD object.

Documentation

structured-data.missing-type

WarningA structured data item has no @type (or itemtype).

What to change: Add "@type" (e.g. "Article") to every structured data object.

Documentation

structured-data.missing-required

WarningA structured data item of a known type is missing properties Google requires for rich results.

What to change: Add the missing properties listed in evidence. Validate with https://search.google.com/test/rich-results after fixing.

Documentation

structured-data.invalid-date

WarningDate properties in structured data that are not ISO 8601.

What to change: Use ISO 8601 dates such as 2026-03-14 or 2026-03-14T09:30:00+01:00.

Documentation

structured-data.retired-type

InfoThe page has structured data for a rich result Google no longer shows: FAQPage, HowTo, SpecialAnnouncement, ClaimReview, the sitelinks search box's SearchAction, Estimated Salary (Occupation), Learning Video (LearningResource on a VideoObject) or Book Actions (ReadAction, BorrowAction on a Book).

What to change: Do not spend effort on this markup for Google: it no longer earns a rich result, and its missing properties are not reported. Keep it if something else reads it (answer engines read FAQPage, for instance), or remove it if it only served the rich result.

Documentation

structured-data.invalid-value

WarningA structured data value is in a form Google cannot use: a price with a currency symbol or a decimal comma, a currency that is not an ISO 4217 code, a rating outside its scale, a breadcrumb trail with fewer than two items, odd positions or crumbs without a URL, or an availability, itemCondition, eventStatus, eventAttendanceMode or employmentType that is not one of the values Google lists.

What to change: Write prices as plain numbers with a dot ("19.99") and the currency in priceCurrency ("EUR"); set bestRating when ratings are not out of 5; number breadcrumb positions 1, 2, 3 and give every crumb but the last an item URL; write availability, itemCondition, eventStatus and eventAttendanceMode as schema.org values ("https://schema.org/InStock"), and employmentType as FULL_TIME, PART_TIME, CONTRACTOR, ... in capitals.

Documentation

structured-data.relative-url

InfoA URL in JSON-LD (url, image, logo, item, sameAs, ...) is relative. JSON-LD does not resolve these properties against the page URL, and Google documents only absolute URLs.

What to change: Write absolute URLs in JSON-LD, built from the site's production URL: https://www.example.com/img/logo.png, not /img/logo.png.

Documentation

structured-data.dangling-id

InfoJSON-LD refers to an object by "@id" that no object on the page defines, so a reader of this page gets the id and none of the object's properties.

What to change: Define the object on every page that refers to it: put it in the same @graph with its @id, @type and properties (an article's author or publisher, for example), or write the properties inline instead of {"@id": ...}.

Documentation

structured-data.paywall-invalid

WarningPaywalled content markup Google cannot use: isAccessibleForFree is false but hasPart names no paywalled section, or the section's cssSelector is not a .class selector (warning); no element in the HTML has that class (info). Without it, Google cannot tell a paywall from cloaking.

What to change: Give the object a hasPart of type WebPageElement with "isAccessibleForFree": false and a cssSelector that is a single class (".paywall"), and give the element that holds the paywalled text that class in the HTML.

Documentation

structured-data.self-serving-review

InfoAn Organization or LocalBusiness on its own site carries aggregateRating or review markup about itself. Google shows no review stars for such self-serving reviews, also when a widget adds them.

What to change: Remove aggregateRating and review from the organization's own markup, or turn off the review widget's markup setting. Keep the reviews on the page; put ratings on the products or services they are about (Product markup), which can earn stars.

Documentation

structured-data.site-name-missing

InfoThe home page has structured data but no WebSite item, which Google reads first for the site name it shows in search results.

What to change: Add WebSite JSON-LD to the home page with the site's name and its home page URL: {"@type": "WebSite", "name": "Example", "url": "https://www.example.com/"}. Add alternateName for a shorter or longer name.

Documentation

Content

4 rules

content.empty

WarningThe HTML contains no visible text.

What to change: If the page is rendered client-side, use server-side rendering or static generation so the HTML carries the content. Re-run with render enabled to compare the rendered DOM.

Documentation

content.thin

WarningThe page has very little text (default: fewer than 300 words).

What to change: Add substantive, useful content, or set noindex if the page is a utility page (login, cart, thank-you). Thin pages rarely rank and can drag down site quality signals.

content.placeholder-text

ErrorPlaceholder text ("lorem ipsum") is present in the page.

What to change: Replace the placeholder copy with real content before publishing.

content.readability-low

InfoFlesch Reading Ease below 30 (very difficult). Only evaluated for English or unspecified language, 100+ words.

What to change: Shorten sentences and prefer common words. Aim for a Flesch score of 50-70 for general audiences.

Keywords

6 rules

keyword.not-in-title

WarningkeywordThe target keyword does not appear in the title.

What to change: Include the target keyword (or a close variant) near the start of the title.

Runs when the audit is given a keyword.

keyword.not-in-h1

WarningkeywordThe target keyword does not appear in any H1.

What to change: Use the target keyword in the H1 so the main heading matches the search intent.

Runs when the audit is given a keyword.

keyword.not-in-description

InfokeywordThe target keyword does not appear in the meta description.

What to change: Mention the keyword in the meta description; matching terms are bolded in search snippets.

Runs when the audit is given a keyword.

keyword.not-in-body

WarningkeywordThe target keyword does not appear in the visible body text.

What to change: Use the keyword naturally in the body copy, ideally in the first paragraph.

Runs when the audit is given a keyword.

keyword.not-in-url

InfokeywordThe target keyword does not appear in the URL path.

What to change: Where the URL is still changeable, use a short slug containing the keyword (hyphen-separated).

Runs when the audit is given a keyword.

keyword.density-high

InfokeywordThe target keyword makes up an unusually large share of the body text (default > 3%).

What to change: Reduce repetition; write for readers and use synonyms. Keyword stuffing can trigger spam filters.

Runs when the audit is given a keyword.

The HTTP response

7 rules

response.error-status

ErrorThe URL returned a 4xx or 5xx status code.

What to change: Fix the route or server error. If the page was removed on purpose, return 410 (or 404) and remove internal links and sitemap entries pointing to it.

response.redirected

InfoThe requested URL redirected to another URL.

What to change: Link directly to the final URL (internal links, sitemap, canonical) so crawlers and users skip the redirect.

Documentation

response.redirect-chain

WarningThe URL goes through two or more redirects before reaching the final page.

What to change: Collapse the chain so the first URL redirects straight to the final destination. Each hop costs crawl budget and loses some signal.

Documentation

response.slow

WarningThe HTML response took longer than the configured threshold (default 2000 ms).

What to change: Reduce server response time: cache the HTML, avoid blocking data fetches during render, use a CDN. Google recommends TTFB under 800 ms.

Documentation

response.not-html

WarningThe response Content-Type is not HTML.

What to change: Serve pages with Content-Type: text/html; charset=utf-8.

response.truncated

InfoThe page is larger than the audit reads (5 MB; 2 MB on the hosted server), so only its first part was audited.

What to change: Findings about the rest of the page are missing, and counts such as words, links and images cover only the part that was read. HTML this large is slow for crawlers too: make it smaller (document.html-too-large says how). On the hosted server, the local server reads up to 5 MB.

response.not-https

WarningThe final URL is served over plain http.

What to change: Serve the site over https and redirect http to https. HTTPS is a (light) ranking signal and required for many browser features.

JavaScript rendering

12 rules

rendering.title-client-only

Warning--renderThe <title> only exists after JavaScript runs.

What to change: Render this in the server HTML (SSR/SSG/prerender). Googlebot queues JavaScript rendering separately and other crawlers and link previews never run it.

Runs with --render, on the command or the local server.

Documentation

rendering.title-changed

Info--renderJavaScript changes the <title> after load.

What to change: Make the server HTML title match the final title so both agree.

Runs with --render, on the command or the local server.

Documentation

rendering.description-client-only

Warning--renderThe meta description only exists after JavaScript runs.

What to change: Render this in the server HTML (SSR/SSG/prerender). Googlebot queues JavaScript rendering separately and other crawlers and link previews never run it.

Runs with --render, on the command or the local server.

Documentation

rendering.canonical-client-only

Warning--renderThe canonical link only exists after JavaScript runs.

What to change: Render this in the server HTML (SSR/SSG/prerender). Googlebot queues JavaScript rendering separately and other crawlers and link previews never run it.

Runs with --render, on the command or the local server.

Documentation

rendering.canonical-changed

Warning--renderJavaScript changes the canonical URL after load, sending conflicting signals.

What to change: Emit the final canonical in the server HTML and do not modify it client-side.

Runs with --render, on the command or the local server.

Documentation

rendering.noindex-client-only

Error--renderA noindex directive is injected by JavaScript. Google applies it when it renders the page, which removes the page from the index.

What to change: Remove the client-side noindex (often a framework/head-manager default or an auth guard) or move the decision to the server.

Runs with --render, on the command or the local server.

Documentation

rendering.noindex-removed-by-js

Warning--renderThe server HTML says noindex but JavaScript removes it. Google honours the server noindex and skips rendering, so the page stays out of the index.

What to change: Remove noindex from the server HTML if the page should be indexed.

Runs with --render, on the command or the local server.

Documentation

rendering.h1-client-only

Warning--renderThe <h1> only exists after JavaScript runs.

What to change: Render this in the server HTML (SSR/SSG/prerender). Googlebot queues JavaScript rendering separately and other crawlers and link previews never run it.

Runs with --render, on the command or the local server.

Documentation

rendering.content-client-only

Warning--renderMost of the body text only exists after JavaScript runs.

What to change: Render this in the server HTML (SSR/SSG/prerender). Googlebot queues JavaScript rendering separately and other crawlers and link previews never run it.

Runs with --render, on the command or the local server.

Documentation

rendering.structured-data-client-only

Info--renderStructured data is only added by JavaScript.

What to change: Google processes JSON-LD injected by JavaScript, but other consumers do not. Prefer emitting it in the server HTML.

Runs with --render, on the command or the local server.

Documentation

rendering.console-errors

Info--renderThe page logged JavaScript errors while rendering.

What to change: Fix runtime errors; a failing script can leave crawlers with an incomplete page.

Runs with --render, on the command or the local server.

Documentation

Site-wide checks

49 rules

site.robots-txt-missing

WarningcrawlNo robots.txt was found at the site root.

What to change: Add /robots.txt. At minimum: "User-agent: *" with no disallow rules, plus a "Sitemap:" line.

Runs on a crawl or a build directory.

Documentation

site.robots-txt-error

Warningcrawlrobots.txt returned a server error or could not be fetched. Google treats 5xx robots.txt as "do not crawl anything".

What to change: Make /robots.txt return 200 (or 404 if you have none).

Runs on a crawl or a build directory.

Documentation

site.robots-txt-blocks-all

Errorcrawlrobots.txt disallows crawling of the whole site for all user agents.

What to change: Remove "Disallow: /" for "User-agent: *" unless the site is intentionally private (staging).

Runs on a crawl or a build directory.

Documentation

site.robots-blocked-pages

WarningcrawlPages linked internally (or listed in the sitemap) are disallowed by robots.txt.

What to change: Allow the paths in robots.txt, or stop linking to them and remove them from the sitemap if they should stay hidden.

Runs on a crawl or a build directory.

Documentation

site.robots-txt-too-large

Warningcrawlrobots.txt is larger than 500 KiB; Google ignores everything after the first 500 KiB.

What to change: Shorten robots.txt: group paths under shared prefixes, use wildcards (Disallow: /*?sort=) instead of listing URLs one by one, and remove rules for paths that no longer exist.

Runs on a crawl or a build directory.

Documentation

site.robots-txt-unsupported-rule

Warningcrawlrobots.txt uses rules Google does not support: noindex or nofollow lines (retired in 2019, so the pages stay indexable; warning) or crawl-delay (Google ignores it, Bing reads it; info).

What to change: Remove noindex: and nofollow: lines and use a robots meta tag or X-Robots-Tag header on the pages instead. Crawl-delay may stay for Bing and other crawlers; Google sets its crawl rate itself.

Runs on a crawl or a build directory.

Documentation

site.robots-txt-invalid-rule

Warningcrawlrobots.txt has lines crawlers cannot use: an Allow or Disallow path that starts with neither / nor * (a full URL included), which matches no URL; an Allow or Disallow above the first User-agent line, which belongs to no group; or a Sitemap that is not a full URL, which search engines ignore.

What to change: Start every Allow and Disallow path with / (Disallow: /wp-admin/, not wp-admin/ or https://example.com/wp-admin/), put every rule under a User-agent line, and write each Sitemap line as a full URL (Sitemap: https://example.com/sitemap.xml).

Runs on a crawl or a build directory.

Documentation

site.robots-blocks-resources

Warningcrawlrobots.txt keeps Googlebot from scripts or stylesheets the pages load, so Google renders the pages without them.

What to change: Allow the CSS and JavaScript files pages need (Allow: /assets/, or remove the Disallow line for that folder). Keep blocking only what pages do not need to render.

Runs on a crawl or a build directory.

Documentation

site.sitemap-missing

WarningcrawlNo XML sitemap was found (robots.txt Sitemap: line or /sitemap.xml).

What to change: Generate a sitemap.xml listing canonical, indexable URLs and reference it from robots.txt.

Runs on a crawl or a build directory.

Documentation

site.sitemap-error

ErrorcrawlA sitemap could not be fetched or parsed.

What to change: Make the sitemap return 200 with valid XML (urlset or sitemapindex).

Runs on a crawl or a build directory.

Documentation

site.sitemap-too-large

ErrorcrawlA sitemap holds more than 50,000 URLs or more than 50 MB uncompressed, which search engines do not read (error); or it is larger than this audit reads (15 MB; 5 MB on the hosted server), so it was not checked (info).

What to change: Split the sitemap into files of at most 50,000 URLs and 50 MB each, and list them in a sitemap index.

Runs on a crawl or a build directory.

Documentation

site.sitemap-index-nested

WarningcrawlA sitemap index lists another sitemap index; Google reads only sitemaps from an index, not further indexes.

What to change: List every sitemap file directly in one sitemap index (up to 50,000), or submit each index separately in Search Console.

Runs on a crawl or a build directory.

Documentation

site.sitemap-invalid-url

WarningcrawlSitemap <loc> values that are not full http(s) URLs, or are 2,048 characters or longer. Search engines crawl sitemap URLs exactly as listed and drop these, so the pages they name are not submitted.

What to change: Write every <loc> as a full URL with protocol and host (https://www.example.com/about/, not /about/), under 2,048 characters, generated from the production site URL.

Runs on a crawl or a build directory.

Documentation

site.sitemap-lastmod-invalid

WarningcrawlSitemap <lastmod> values are not in W3C Datetime format, so search engines ignore them.

What to change: Write <lastmod> as a date (2026-11-09) or a date and time with its time zone (2026-11-09T10:00:00+01:00).

Runs on a crawl or a build directory.

Documentation

site.sitemap-lastmod-future

InfocrawlSitemap <lastmod> values lie in the future. Google uses lastmod only when it is consistently accurate.

What to change: Set <lastmod> to when the page's content last changed, not to the build time plus an offset or a publish date to come.

Runs on a crawl or a build directory.

Documentation

site.sitemap-lastmod-missing

InfocrawlA sitemap lists URLs but gives none of them a <lastmod>, so search engines cannot tell which pages changed. Bing calls lastmod a key signal for what to crawl again; Google uses it when it is consistently accurate.

What to change: Give each <url> a <lastmod> with the date its content last changed in substance (2026-11-09 or 2026-11-09T10:00:00+01:00), taken from the content, never the time the sitemap is built.

Runs on a crawl or a build directory.

Documentation

site.sitemap-lastmod-uniform

InfocrawlEvery URL of a sitemap of 10 or more has the same <lastmod> date and time, which is the time the sitemap was built rather than when each page changed. Bing asks sites not to set lastmod to the sitemap's build time, and Google ignores lastmod values that are not accurate.

What to change: Set each <lastmod> to when that page's content last changed (the CMS's modified date, the content file's last commit or its front matter), not to the build time.

Runs on a crawl or a build directory.

Documentation

site.sitemap-other-host

InfocrawlThe site's sitemaps list URLs on other hosts. Google accepts them only for hosts verified in Search Console or whose own robots.txt names the sitemap.

What to change: List each host's URLs in a sitemap that host's robots.txt names. If the URLs use another domain by mistake (a staging or old domain), generate them from the production site URL.

Runs on a crawl or a build directory.

Documentation

site.sitemap-noindex-page

ErrorcrawlThe sitemap lists pages that are set to noindex.

What to change: Either remove noindex (if the page should rank) or remove the URL from the sitemap. Conflicting signals waste crawl budget.

Runs on a crawl or a build directory.

Documentation

site.sitemap-non-canonical

WarningcrawlThe sitemap lists URLs whose canonical points to a different URL.

What to change: List only canonical URLs in the sitemap.

Runs on a crawl or a build directory.

Documentation

site.sitemap-broken-url

ErrorcrawlThe sitemap lists URLs that return 4xx/5xx.

What to change: Remove dead URLs from the sitemap or fix them.

Runs on a crawl or a build directory.

Documentation

site.sitemap-redirecting-url

WarningcrawlThe sitemap lists URLs that redirect.

What to change: Replace redirecting URLs in the sitemap with their final destination.

Runs on a crawl or a build directory.

Documentation

site.not-in-sitemap

InfocrawlIndexable pages discovered by crawling are not listed in the sitemap.

What to change: Add the missing URLs to the sitemap (or generate it from the router so it stays complete).

Runs on a crawl or a build directory.

Documentation

site.orphan-pages

InfocrawlSitemap URLs that no crawled page links to.

What to change: Link orphan pages from relevant pages or navigation; pages without internal links get little authority and are crawled less.

Runs on a crawl or a build directory.

Documentation

site.redirect-chains

WarningcrawlInternal URLs go through two or more redirects (or loop).

What to change: Point each redirect straight at its final destination.

Runs on a crawl or a build directory.

Documentation

site.duplicate-titles

WarningcrawlSeveral indexable pages share the same <title>.

What to change: Give each page a unique title that describes that page (templates: include the item name, category and brand).

Runs on a crawl or a build directory.

Documentation

site.duplicate-descriptions

WarningcrawlSeveral indexable pages share the same meta description.

What to change: Write a unique description per page, or generate it from page-specific data.

Runs on a crawl or a build directory.

Documentation

site.duplicate-h1

InfocrawlSeveral pages share the same <h1>.

What to change: Make H1s page-specific.

Runs on a crawl or a build directory.

site.duplicate-content

WarningcrawlPages with identical or near-identical body text.

What to change: Consolidate duplicates with a canonical to one URL, 301 redirect, or make the content distinct.

Runs on a crawl or a build directory.

Documentation

site.deep-pages

InfocrawlPages that are more than the configured number of clicks (default 3) from the start URL.

What to change: Flatten the architecture: link important deep pages from category pages, navigation or the homepage.

Runs on a crawl or a build directory.

Documentation

site.soft-404

WarningcrawlA clearly non-existent URL returns HTTP 200 instead of 404.

What to change: Return a real 404 (or 410) status for unknown routes; otherwise search engines index error pages and waste crawl budget.

Runs on a crawl or a build directory.

Documentation

site.http-not-redirected

WarningcrawlThe http:// version of the site does not redirect to https://.

What to change: Add a permanent (301/308) redirect from http to https at the server or CDN.

Runs on a crawl or a build directory.

Documentation

site.host-variant-not-redirected

WarningcrawlThe www/non-www variant of the host does not redirect to the main host.

What to change: Redirect the alternate host (www or bare domain) to the canonical host with a 301/308.

Runs on a crawl or a build directory.

Documentation

site.unlinked-pages

InfocrawlHTML files in the build output that no other page links to.

What to change: Link the pages from navigation or related content, or remove them from the build if they are obsolete.

Runs on a crawl or a build directory.

Documentation

site.canonical-target-broken

ErrorcrawlPages name a canonical URL that answers 4xx/5xx or does not load, so search engines cannot use it.

What to change: Point the canonical at the live URL of the preferred page, usually the page itself. Canonicals built from a route pattern often miss a trailing slash or a locale prefix.

Runs on a crawl or a build directory.

Documentation

site.canonical-target-redirects

WarningcrawlPages name a canonical URL that redirects; the canonical should be the final URL.

What to change: Write the URL the redirect ends at in the canonical: the same scheme, host, trailing slash and case the server answers with.

Runs on a crawl or a build directory.

Documentation

site.canonical-target-noindex

WarningcrawlPages name as canonical a page that is set to noindex, so the page meant to be indexed is kept out of the index.

What to change: Remove noindex from the canonical page, or point the canonical somewhere indexable. If both pages should stay out of search results, use noindex on both and drop the canonical.

Runs on a crawl or a build directory.

Documentation

site.canonical-chain

WarningcrawlA page's canonical points to a page whose own canonical points somewhere else (A to B to C).

What to change: Point every page's canonical straight at the final preferred URL.

Runs on a crawl or a build directory.

Documentation

site.hreflang-target-invalid

Warningcrawlhreflang alternates point to URLs that are broken, redirect, are noindex or name another URL as canonical, so the annotation is ignored.

What to change: List only the canonical, indexable URL of each language version in hreflang: the final URL, not one that redirects.

Runs on a crawl or a build directory.

Documentation

site.url-variant-duplicate

WarningcrawlA page also answers 200 at its URL with the trailing slash added or removed, or with the path in another case, without redirecting or naming the page as canonical. Google treats each spelling as a separate URL.

What to change: Redirect (301) the other spelling to the page's URL in the server, framework or CDN (trailingSlash in Next.js, Nuxt and Astro configs; a lowercase rewrite on case-insensitive servers), or at least give every spelling a canonical to the one URL.

Runs on a crawl or a build directory.

Documentation

site.parameter-duplicates

WarningcrawlCrawled URLs that differ only in their query string show the same text, and none names one of them as canonical.

What to change: Give the parameter versions a canonical to the URL without the parameter (or the one you want indexed), and link to that URL. Parameters that change nothing visible (session, sort order of one item, view=) should not reach links at all.

Runs on a crawl or a build directory.

Documentation

site.broken-images

WarningcrawlImages on the pages point to URLs that answer 4xx/5xx or do not load.

What to change: Fix the image URLs listed in evidence, or upload the missing files. Each entry names the pages that show the image.

Runs on a crawl or a build directory.

Documentation

site.crawl-limit-reached

InfocrawlThe crawl stopped at the page limit, so site-wide checks (orphans, duplicates, sitemap coverage) are partial.

What to change: Re-run with a higher maxPages value for a complete picture.

Runs on a crawl or a build directory.

site.crawl-stopped

InfocrawlThe hosted server stopped requesting the site before the crawl was done: the site refused its requests (a challenge, HTTP 429), robots.txt asked for a Crawl-delay longer than a hosted crawl waits, or the server reached its request limit for the site. Site-wide checks (broken links, duplicates, sitemap coverage) are partial.

What to change: The message says which. The hosted server's own limit for a site lifts within a minute: crawl again then. A refusal or a Crawl-delay is the site's choice, which the hosted server keeps.

Runs on a crawl or a build directory.

AI answer engines

32 rules

aeo.page-error

ErrorThe page answers with an error status or could not be fetched, so no crawler can read it.

What to change: Make the URL answer 200 with the page's HTML. Answer engines, like search engines, drop pages that fail.

Documentation

aeo.page-truncated

InfoThe page is larger than the audit reads (5 MB; 2 MB on the hosted server), so only its first part was audited.

What to change: Findings about the rest of the page are missing. HTML this large is slow for crawlers too: move inline scripts, data and SVG into files the page loads. On the hosted server, the local server reads up to 5 MB.

aeo.noindex

WarningThe page is set to noindex, so answer engines that draw on a search index cannot cite it.

What to change: Remove noindex if answer engines should quote this page. Keep it on pages that should stay private (login, cart, internal search); preview hosts often add it with X-Robots-Tag, so check production first.

Documentation

aeo.nosnippet

Warningnosnippet (or max-snippet:0) keeps the page's text out of Google's AI Overviews and AI Mode and limits what Copilot can cite.

What to change: Remove nosnippet or max-snippet:0 if answer engines should quote this page. To hide one part only, wrap that part in an element with data-nosnippet.

Documentation

aeo.max-snippet-short

Infomax-snippet is below 100 characters, which also caps what Google quotes in AI Overviews and AI Mode.

What to change: Raise max-snippet, or set max-snippet:-1 for no limit, so answer engines can quote whole sentences.

Documentation

aeo.noarchive

Warningnoarchive keeps the page out of Microsoft Copilot answers; Bing still lists it in search.

What to change: Remove noarchive if the page should appear in Copilot answers. Paywalled pages can use nocache instead, which lets Copilot cite the URL, title and snippet. Google ignores noarchive.

Documentation

aeo.nocache

Infonocache limits Microsoft Copilot to the page's URL, title and snippet.

What to change: Remove nocache if Copilot should quote the page's content, not only link to it.

Documentation

aeo.noai

InfoThe page sets noai or noimageai, directives no major answer engine documents support for.

What to change: To opt out of AI training, say so in robots.txt (a `User-Agent: __usage_ai-training` group with `Disallow: /`, or groups for GPTBot, ClaudeBot and Google-Extended). If the page should appear in AI answers, remove noai to avoid mixed signals.

aeo.data-nosnippet

Infodata-nosnippet hides a large share of the page's text (20% or more) from snippets and AI Overviews.

What to change: Keep data-nosnippet on boilerplate or paywalled parts only. The text answer engines should quote must be outside it.

Documentation

aeo.text-needs-javascript

WarningThe HTML is an empty JavaScript app shell; most AI crawlers do not run JavaScript, so they see no content.

What to change: Render the content on the server (SSR or static generation) so the HTML carries it. `agentic-seo-check audit <url> --render` compares the HTML with the rendered page.

Documentation

aeo.question-not-answered-first

InfoA heading asks a question but no short, direct answer follows it.

What to change: Answer each question in the first sentence or two under its heading (under about 60 words), then add detail. A direct answer right under the question is the passage answer engines find easiest to quote.

aeo.unstructured-text

InfoA long page (600+ words) has no subheadings, lists or tables.

What to change: Break the text into sections under descriptive subheadings, and use lists or tables for steps, options and comparisons, so answer engines can lift one self-contained passage.

aeo.schema-text-not-visible

WarningFAQPage, QAPage or HowTo structured data holds questions, answers or steps that are not in the page's HTML text.

What to change: Mark up only what the page shows: render every question, answer and step from the same data as the JSON-LD, or remove the items that are not on the page. Google's structured data guidelines forbid marking up content readers cannot see. If the page adds them with JavaScript, render them on the server too: most AI crawlers do not run JavaScript.

Documentation

aeo.article-author-missing

WarningArticle structured data names no author.

What to change: Add author as a Person (or Organization) with name and url, the url pointing to an author page. Answer engines weigh who stands behind the content.

Documentation

aeo.article-author-incomplete

InfoThe article's author is a bare name, without a url or sameAs that identifies the person.

What to change: Write the author as {"@type": "Person", "name": ..., "url": ...}, with url pointing to an author page, and sameAs listing the person's profiles where they exist.

Documentation

aeo.article-dates-missing

WarningArticle structured data lacks datePublished (warning) or dateModified (info), which tell answer engines how fresh the content is.

What to change: Add datePublished and dateModified in ISO 8601 (2026-03-14T09:30:00+01:00), and update dateModified when the content changes materially.

Documentation

aeo.article-date-not-visible

InfoThe article's structured data has a date, but the page shows none.

What to change: Show the publish or update date near the title, matching the structured data; a <time datetime> element works well. Readers and answer engines use it to judge freshness.

Documentation

aeo.organization-missing

InfoThe home page has no Organization (or Person) structured data.

What to change: Add Organization JSON-LD to the home page (name, url, logo, and sameAs links to official profiles) so answer engines can tie the site to a known entity. Use Person for a personal site.

Documentation

aeo.entity-same-as-missing

InfoOrganization or Person structured data has no sameAs links to the entity's profiles elsewhere.

What to change: Add sameAs with the URLs of the entity's official profiles (Wikipedia, Wikidata, LinkedIn, social accounts) so answer engines can match it to the entity they know.

Documentation

aeo.robots-txt-unreachable

Warningrobots.txt answers a server error or does not answer, and crawlers treat that as "crawl nothing" (RFC 9309).

What to change: Make /robots.txt answer 200, or 404 when there is none. A 5xx or a timeout stops well-behaved crawlers, AI crawlers included.

Documentation

aeo.robots-blocks-search-crawler

Errorrobots.txt blocks a search crawler whose index AI answers draw on: Googlebot (AI Overviews, AI Mode), Bingbot (Copilot) or Applebot (Siri, Spotlight).

What to change: Allow these crawlers in robots.txt: remove the Disallow lines that match the audited pages in their groups, or in `User-agent: *` when they have no group of their own. Applebot follows the Googlebot group when no group names Applebot.

Documentation

aeo.robots-blocks-answer-bot

Warningrobots.txt blocks a bot that fetches pages for AI answers or an AI search index (OAI-SearchBot, ChatGPT-User, Claude-SearchBot, Claude-User, PerplexityBot, Perplexity-User, Google-Extended, DuckAssistBot, MistralAI-User, Amzn-SearchBot, Amzn-User, meta-webindexer, meta-externalfetcher, MistralAI-Index, YandexAdditional).

What to change: Allow the bots listed in evidence if the site should be cited in those answer engines: add a group for each with `Allow: /`, or remove the Disallow lines that match them. Keep a block that is deliberate, and disable this rule.

Documentation

aeo.robots-blocks-training-bot

Inforobots.txt blocks a crawler that collects text to train AI models (GPTBot, ClaudeBot, Applebot-Extended, CCBot, meta-externalagent, Amazonbot, MistralAI-Training).

What to change: Nothing to change if you mean to opt out of model training: these bots do not decide whether answer engines cite the site. Allow them if you want the content in future models.

Documentation

aeo.content-signal-blocks-answers

WarningA robots.txt Content-Signal line sets ai-input=no or search=no, asking answer engines not to use the pages in AI answers or search.

What to change: Set ai-input=yes and search=yes in the Content-Signal line if answer engines should use the content; ai-train=no can stay to opt out of training. Example: `Content-Signal: search=yes, ai-input=yes, ai-train=no`.

Documentation

aeo.content-signal-invalid

InfoA robots.txt Content-Signal line has a signal other than search, ai-input or ai-train, or a value other than yes or no.

What to change: Write Content-Signal as comma-separated pairs of search, ai-input or ai-train with yes or no, e.g. `Content-Signal: search=yes, ai-input=yes, ai-train=no`.

Documentation

aeo.llms-txt-missing

InfoThe site has no /llms.txt.

What to change: Optional: publish /llms.txt, a Markdown index of the pages that matter, for AI tools and coding agents that look for one. No major answer engine has said it reads llms.txt, so this is low priority.

Documentation

aeo.llms-txt-invalid

Warning/llms.txt exists but does not follow the llms.txt format, which requires an H1 title first.

What to change: Start the file with `# Site name`, then an optional `> one-line summary`, then H2 sections with links written as `- [Title](https://...): note`.

Documentation

aeo.server-blocks-answer-bot

Warning--ai-botsThe server refuses or challenges an AI answer bot's User-Agent (403, 429, a challenge page) while it serves the audit's own request.

What to change: Allow these bots at the CDN, firewall or bot-protection layer (for example the CDN's AI crawler settings or a WAF rule). The probe sent the bots' User-Agent from this machine, not from the bots' own IP ranges, and a CDN that verifies bots may let the real ones through: confirm in the CDN's bot logs before changing rules.

Runs with --ai-bots, on the command or the local server.

aeo.server-blocks-training-bot

Info--ai-botsThe server refuses or challenges an AI training bot's User-Agent while it serves the audit's own request.

What to change: Nothing to change if you mean to keep training bots out; this does not affect AI answers. The probe came from this machine, not the bots' own IP ranges.

Runs with --ai-bots, on the command or the local server.

aeo.crawl-stopped

InfoThe hosted server stopped requesting the site before the crawl was done: the site refused its requests (a challenge, HTTP 429), robots.txt asked for a Crawl-delay longer than a hosted crawl waits, or the server reached its request limit for the site. The audit covers the pages it read.

What to change: The message says which. The hosted server's own limit for a site lifts within a minute: crawl again then. A refusal or a Crawl-delay is the site's choice, which the hosted server keeps.

aeo.bot-probe-inconclusive

Info--ai-botsThe AI bot probe could not run: the target is not a URL, or the audit's own request did not get the page.

What to change: Probe a live URL, and make the audit's own request succeed first (status, auth headers, --user-agent); otherwise blocked bots cannot be told apart from a page that fails for everyone.

Runs with --ai-bots, on the command or the local server.