Methodology

Every check, every threshold, every deduction — published.

The audit is deterministic: the same site on the same day produces the same findings and the same scores. No opinion layer decides what fails, and no language model touches a score. This page lists what the audit fetches, what each check fires on, and exactly how the numbers combine. The limits and threshold bands below are rendered from the same constants the engine imports, so the page cannot drift from the code that produces your report.

Think a finding on your report is wrong? Email the report link — a person re-runs the check and answers.

What we fetch

The crawl contract

One audit fetches up to 20 pages of the submitted site, then runs three small probe phases — and nothing else. Every request stays on the audited site.

PhaseBudgetWhat happens
Page crawl up to 20 requests, 45 s Homepage first, then robots.txt, llms.txt and the sitemap. The remaining budget goes to same-site pages — sitemap URLs first (the site's own canonical page list), then navigation links.
Internal-link probes up to 10 requests, 10 s HEAD checks (status and headers only, no body) on same-site link targets the crawl did not fetch. Links the budget never reaches are reported as unchecked — never judged broken.
Sitemap sample up to 5 requests, 6 s HEAD checks on a sample of sitemap URLs the crawl did not reach. The report names how many were listed and how many were actually checked.
Asset sizing up to 8 requests, 8 s Sizes same-site CSS, script and image files from response headers (plus one single-byte ranged request when a server hides the size). Assets on other domains are counted but never fetched; assets that could not be sized are never guessed.
Rule on every requestLimit
Protocols and portshttp/https only, standard web ports only, no credentials in URLs
Address guardEvery hostname is resolved and every address must be publicly routable — private, internal, loopback and reserved addresses are refused. Every redirect hop is re-validated the same way.
Redirectsat most 10 hops per fetch
Response bodyat most 5 MB read per response
Time per fetchat most 15 s wall clock, redirects included
Identificationevery request sends the User-Agent CorpusPlanBot/1.0 (+https://corpushq.co/plan)

How the numbers combine

The scoring formula, in plain words

Each pillar starts at 100. Two layers take points off — binary findings and graded quality measurements — and both are written out on the report, line by line.

1. Binary findings

Every finding in the tables below deducts a fixed amount from its pillar. Deductions add up without limit (floor 0), so every additional problem costs something.

SeverityDeduction
Critical35 points
Major15 points
Minor5 points

One critical alone caps a pillar at 65.

2. Graded quality curve

Each pillar also measures quality signals scored 0.0–1.0 against the written bands below. The pillar's graded deduction is 40 × (1 − weighted mean of the measured signals). Signals that could not be measured leave the denominator entirely — a pillar with nothing measurable takes no graded deduction. A site whose measured signals all hit their top band deducts nothing and can score 100; no measurement is ever invented to avoid that.

Every graded cost of 0.5 points or more appears on the report as a written note carrying the measured value and the exact point cost — no score moves without a written reason.

Pillar score and overall

pillar = 100 − finding deductions − graded deduction, clamped to 0–100 and rounded. The overall score is the weighted sum of the five pillars.

PillarWeight in the overall score
SEO25%
Speed15%
Trust20%
Content15%
AI citability25%

SEO and GEO carry the most weight because search and AI-assistant visibility are what the product is bought for; trust is next because broken TLS or missing contact routes lose visitors you already won; speed and content are real factors but usually the symptom rather than the disease. The weighting is our judgment and is stated here as exactly that.

Pillar · weight 25%

SEO

Classic search fundamentals, checked on the homepage and swept across every crawled page. Each finding names the affected paths as evidence.

CheckCodeSeverityFires when
Homepage titleSEO_MISSING_TITLECriticalThe homepage has no <title> tag.
Title lengthSEO_TITLE_LENGTHMinorThe homepage title is outside 15–65 characters.
noindexSEO_NOINDEX_HOMEPAGECriticalA robots meta tag with noindex tells engines to drop the homepage from their index.
Meta descriptionSEO_MISSING_META_DESCRIPTIONMajorThe homepage has no meta description.
Description lengthSEO_META_DESCRIPTION_LENGTHMinorThe meta description is outside 50–165 characters.
H1 headingSEO_MISSING_H1MajorThe homepage has no H1.
Multiple H1SEO_MULTIPLE_H1MinorThe homepage carries more than one H1.
SubheadingsSEO_NO_SUBHEADINGSMinorNo H2/H3 subheadings on the homepage.
Canonical linkSEO_MISSING_CANONICALMinorNo rel="canonical" link tag on the homepage.
Image alt textSEO_IMG_ALT_MISSINGMajor / MinorUnder 50% of sampled homepage images carry alt text (major); 50–80% (minor). Sample: the first 30 images.
robots.txt blocks allSEO_ROBOTS_BLOCKS_ALLCriticalrobots.txt disallows the whole site for every user-agent.
robots.txt is HTMLSEO_ROBOTS_NOT_TEXTMinor/robots.txt answers with an HTML page instead of a plain-text file.
robots.txt missingSEO_MISSING_ROBOTSMinor/robots.txt answers 404.
Sitemap invalidSEO_SITEMAP_INVALIDMinorA sitemap responds but is not valid XML.
Sitemap missingSEO_MISSING_SITEMAPMinorThe sitemap answers 404.
Pages without titlesSEO_PAGES_MISSING_TITLEMajorCrawled inner pages with no <title>, each path named.
Page title lengthsSEO_PAGES_TITLE_LENGTHMinorInner-page titles outside 15–65 characters.
Pages without descriptionsSEO_PAGES_MISSING_META_DESCRIPTIONMinorCrawled inner pages with no meta description.
Pages without H1SEO_PAGES_MISSING_H1MinorCrawled inner pages with no H1.
Pages with multiple H1SEO_PAGES_MULTIPLE_H1MinorCrawled inner pages with more than one H1.
Duplicate titlesSEO_DUPLICATE_TITLESMajorThe same title on two or more crawled pages (post-redirect duplicates of the same document are excluded).
Duplicate descriptionsSEO_DUPLICATE_META_DESCRIPTIONSMinorThe same meta description shared by two or more crawled pages.
Broken internal linksSEO_BROKEN_INTERNAL_LINKSMajorProbed internal links answering HTTP 404/410. Only links that were actually probed and answered are judged; the finding states how many were checked out of how many exist.
Dead sitemap URLsSEO_SITEMAP_DEAD_URLSMajorSampled sitemap URLs answering HTTP 404/410 — same only-what-was-checked rule.
Graded signalCodeWeightScored againstBasis
Title lengthSEO_GRADE_TITLE1.0missing → 0; 30–60 characters → 1.0; else within 15–65 → 0.75; outside → 0.4Google truncates result titles around 60 characters (published guidance); the partial-credit steps are our judgment.
Meta description lengthSEO_GRADE_META_DESCRIPTION1.0missing → 0; 70–155 characters → 1.0; else within 50–165 → 0.75; outside → 0.4Google cuts descriptions around 155–160 characters on desktop results; steps our judgment.
Heading structureSEO_GRADE_HEADINGS1.0one H1 with ordered levels → 1.0; −0.25 for no H2/H3; −0.25 for skipped levels; multiple H1 → 0.5; no H1 → 0.2Our judgment: an ordered outline is what crawlers and screen readers parse best.
Canonical linkSEO_GRADE_CANONICAL0.5present → 1.0; absent → 0Plain presence check.
Internal links from the homepageSEO_GRADE_INTERNAL_LINKS1.0≥ 8 path(s) → 1.0; ≥ 4 path(s) → 0.85; ≥ 1 path(s) → 0.6; below every band → 0Our judgment: a normal navigation plus footer links eight or more distinct pages.

Pillar · weight 15%

Speed

Server response time, page weight, compression and mobile setup — measured from the actual fetches, never estimated from a database.

CheckCodeSeverityFires when
Server response timeSPEED_SLOW_RESPONSEMajor / MinorThe homepage takes 2.0 s or more to start responding (major); 0.8 s or more (minor).
Homepage HTML weightSPEED_HEAVY_HTMLMajor / MinorThe homepage HTML alone is 3072 KB or more (major); 1536 KB or more (minor).
Text compressionSPEED_NO_COMPRESSIONMajorThe homepage response carries no gzip/brotli/zstd/deflate Content-Encoding header.
Mobile viewportSPEED_NO_VIEWPORTMajorNo viewport meta tag — the site renders as a shrunken desktop page on phones.
Slow inner pagesSPEED_SLOW_PAGESMinorThe median response time across three or more crawled inner pages is 2.0 s or more.
Redirect chainSPEED_REDIRECT_CHAINMinorThe homepage settles only after more than 2 redirects; one hop (http to https, or apex to www) is normal.
Oversized assetsSPEED_HEAVY_ASSETMinorA sampled homepage asset transfers 1024 KB (1 MB) or more, named with its measured size. Assets the budget never sized are not judged.
Graded signalCodeWeightScored againstBasis
Server response timeSPEED_GRADE_TTFB1.0≤ 0.2 s → 1.0; ≤ 0.4 s → 0.9; ≤ 0.6 s → 0.75; ≤ 0.8 s → 0.6; ≤ 1.5 s → 0.35; ≤ 2.5 s → 0.15; past every band → 0Lighthouse's server-response-time audit flags responses over 600 ms; the Chrome UX Report "good" threshold is 800 ms. Band steps are our interpolation.
Homepage HTML weightSPEED_GRADE_HTML_WEIGHT1.0≤ 50 KB → 1.0; ≤ 100 KB → 0.85; ≤ 200 KB → 0.6; ≤ 500 KB → 0.35; ≤ 1024 KB → 0.15; past every band → 0HTTP Archive Web Almanac: the median HTML document transfers around 30 KB.
Inline CSS/JS in the HTMLSPEED_GRADE_INLINE_BLOAT1.0≤ 15 KB → 1.0; ≤ 40 KB → 0.75; ≤ 100 KB → 0.5; ≤ 250 KB → 0.25; past every band → 0Critical-CSS guidance keeps inlined assets near the ~14 KB first TCP round trip; band steps our judgment. JSON data blocks (including the JSON-LD the GEO pillar asks for) are excluded.
Text compressionSPEED_GRADE_COMPRESSION0.5compressed → 1.0; none → 0Plain presence check on the Content-Encoding header.
Homepage image countSPEED_GRADE_IMG_COUNT0.5≤ 15 image(s) → 1.0; ≤ 30 image(s) → 0.85; ≤ 50 image(s) → 0.6; ≤ 80 image(s) → 0.35; past every band → 0.15HTTP Archive Web Almanac media chapter: the median page carries around 20–25 images.
Estimated page weight with assetsSPEED_GRADE_PAGE_WEIGHT1.0≤ 512 KB → 1.0; ≤ 1024 KB → 0.9; ≤ 2048 KB → 0.7; ≤ 4096 KB → 0.4; ≤ 8192 KB → 0.2; past every band → 0.1HTTP Archive: the median page transfers about 2–2.5 MB in total; Lighthouse flags multi-MB payloads. Extrapolated from the sampled assets and labeled as an estimate on the report; omitted entirely when assets exist but none could be sized.

Pillar · weight 20%

Trust

TLS, security headers, and the signals buyers look for before they call.

CheckCodeSeverityFires when
TLS certificateTRUST_TLS_ERRORCriticalHTTPS failed with a certificate or TLS error and the site was only reachable over insecure HTTP.
HTTPSTRUST_NO_HTTPSCriticalThe site is served over plain HTTP — it never answered over HTTPS, or the homepage lands on an http:// URL.
Homepage statusTRUST_HOMEPAGE_HTTP_ERRORCriticalThe homepage answers an HTTP error status (400 or higher).
HSTSTRUST_MISSING_HSTSMinorAn HTTPS site without a Strict-Transport-Security header.
Security headersTRUST_MISSING_SECURITY_HEADERSMinorX-Content-Type-Options, or X-Frame-Options/Content-Security-Policy, missing from the homepage response.
Mixed contentTRUST_MIXED_CONTENTMajorScripts, images, iframes, stylesheets or form targets loading over plain http:// on HTTPS pages — every crawled page checked, affected pages named.
Contact routesTRUST_NO_CONTACT_INFOMajorNo phone number, email address or contact link found on the homepage.
Call to actionTRUST_NO_CTAMajorNo book/call/quote/buy/contact wording in any homepage link or button.
Legal pagesTRUST_NO_LEGAL_PAGESMinorNo privacy policy link, or no terms link.
Broken pagesTRUST_BROKEN_PAGESMajorCrawled pages answering HTTP errors (the robots.txt/llms.txt/sitemap probes are reported by their own pillars, not here).
Graded signalCodeWeightScored againstBasis
Trust-signal completenessTRUST_GRADE_SIGNALS2.0signals present ÷ 8, across: phone, email, contact link, call to action, privacy link, terms link, a social profile link, Open Graph tagsA plain ratio; the eight-signal list is our judgment. The report names exactly which signals are missing.
Security headersTRUST_GRADE_SECURITY_HEADERS1.0headers set ÷ headers expected (X-Content-Type-Options; X-Frame-Options or CSP; plus Strict-Transport-Security on HTTPS)A plain ratio over baseline headers.

Pillar · weight 15%

Content

Depth, freshness and presentation of the actual copy — counted from the fetched pages.

CheckCodeSeverityFires when
Homepage copy depthCONTENT_THIN_HOMEPAGEMajor / MinorUnder 120 words of visible text on the homepage (major); under 300 (minor).
Thin inner pagesCONTENT_THIN_PAGESMinorMost crawled inner pages carry under 120 words.
Site footprintCONTENT_FEW_PAGESMinorThe sitemap lists fewer than 5 pages.
Copyright freshnessCONTENT_STALE_COPYRIGHTMinorThe newest copyright year on the homepage is two or more years old. No year on the page: no finding.
Open Graph tagsCONTENT_MISSING_OGMinorog:title is missing, or present without og:description or og:image — shared links render bare.
Graded signalCodeWeightScored againstBasis
Homepage copy depthCONTENT_GRADE_HOME_WORDS1.0≥ 800 word(s) → 1.0; ≥ 500 word(s) → 0.9; ≥ 300 word(s) → 0.75; ≥ 120 word(s) → 0.5; below every band → 0.2Our judgment, anchored on the 120/300-word binary thresholds; full credit needs a genuinely substantial page.
Site copy depthCONTENT_GRADE_SITE_WORDS1.0≥ 2500 word(s) → 1.0; ≥ 1200 word(s) → 0.85; ≥ 600 word(s) → 0.65; ≥ 250 word(s) → 0.4; below every band → 0.2Our judgment — the same anchoring, scaled to the handful of pages one audit fetches.
Image alt coverageCONTENT_GRADE_ALT_COVERAGE1.0images with alt text ÷ sampled images (first 30 per page)A plain ratio. No images on the crawled pages: not measured, no judgment.
Copyright-year freshnessCONTENT_GRADE_FRESHNESS0.5current or last year → 1.0; two years old → 0.5; older → 0.2Our judgment (a January page still saying last year is normal).

Pillar · weight 25%

AI citability

Whether AI assistants can read, parse and cite the site. The AI crawlers checked in robots.txt: GPTBot, ClaudeBot, anthropic-ai, PerplexityBot, Google-Extended.

CheckCodeSeverityFires when
llms.txtGEO_MISSING_LLMS_TXTMajor/llms.txt answers with an error — the file that gives assistants a curated guide to the business does not exist.
llms.txt structureGEO_LLMS_TXT_THINMinorAn llms.txt exists but lacks a markdown H1 title, or has neither links nor 200+ characters of substance.
Structured dataGEO_MISSING_JSONLDMajorNo schema.org JSON-LD found on any crawled page.
JSON-LD validityGEO_JSONLD_INVALIDMinorJSON-LD blocks exist but none parse as valid JSON.
Business identity schemaGEO_NO_ORG_SCHEMAMinorJSON-LD parses but no entity identifies the business itself (Organization, LocalBusiness, WebSite or similar).
All AI crawlers blockedGEO_ALL_AI_BOTS_BLOCKEDCriticalrobots.txt blocks every one of the checked AI crawlers — the site cannot appear in AI-assistant answers. The finding says plainly this may be intentional.
Some AI crawlers blockedGEO_AI_BOTS_BLOCKEDMajorrobots.txt blocks some of the checked AI crawlers, each named.
Extractable contentGEO_LOW_TEXT_RATIOMajor / MinorThe static HTML exposes under 120 readable words alongside a low text-to-markup ratio — usually JavaScript-rendered content many AI crawlers cannot execute (major); under 250 words (minor).
Question-shaped headingsGEO_NO_QUESTION_HEADINGSMinorNo H2/H3 on any crawled page is phrased as a question.
Answer structureGEO_NO_ANSWER_STRUCTUREMinorThe homepage carries 150+ words but no list or table — nothing structured for an assistant to quote.
Graded signalCodeWeightScored againstBasis
llms.txt qualityGEO_GRADE_LLMS_QUALITY1.0H1 title 0.3 + markdown links 0.3 + "##" sections 0.2 + 200+ characters 0.2The llms.txt spec shape; the mark weights are our judgment. Absent or unfetched file: not measured here (the binary finding prices absence).
Structured-data completenessGEO_GRADE_JSONLD_COMPLETENESS1.0identity entity 0.4 + name 0.2 + url 0.2 + a contact field 0.2Our judgment on which schema.org fields assistants actually lift.
Question-shaped headingsGEO_GRADE_QUESTION_HEADINGS1.0≥ 4 question(s) → 1.0; ≥ 2 question(s) → 0.75; ≥ 1 question(s) → 0.5; below every band → 0Our judgment: one lone question heading is a token gesture; a working FAQ carries four or more.
Lists and tablesGEO_GRADE_ANSWER_STRUCTURE1.0≥ 3 element(s) → 1.0; ≥ 1 element(s) → 0.7; below every band → 0Our judgment: structured fragments are what AI engines lift verbatim.

Honesty rules

What we refuse to do

These rules are enforced in the code, not promised in the copy.

Couldn't verify means no judgment

When a check could not gather its evidence, it stays silent instead of guessing — in all three layers. A pillar check whose probe never completed emits no finding. A graded signal that could not be measured leaves the scoring denominator entirely, so an unmeasured signal can neither help nor hurt. A URL the probe budget never reached is reported as unchecked — never counted as broken.

No invented numbers

Every count, size and timing in a finding is measured from the bytes the audit actually fetched, and the finding names its evidence (paths, sizes, character counts). Sampled and extrapolated values are labeled as exactly that. The benchmark median on reports renders only once at least 20 distinct real sites have been audited — below that, nothing renders, because a median of a handful of sites is noise.

The checks never ask a model

Findings and scores come from deterministic code with no language model in the loop. A model only phrases the action plan (and, on Pro, fix hints), grounded solely in the findings it is handed — it cannot add a finding or move a score, and if it fails, a deterministic plan built straight from the findings renders instead.

One model, one moment

The Pro AI-visibility check asks one language model the kinds of questions your customers ask and records whether your business appears. It is labeled as one model at one moment — a snapshot, never a guarantee of rankings or AI mentions. Scores describe what the checks found on the day they ran, nothing more.

Accountability

Report an inaccurate finding

If a finding on your report is wrong, email the report link — a person re-runs the check and answers.

Email [email protected]

Subject line "AUDIT ACCURACY" is pre-filled. Because the audit is deterministic, the disputed check re-runs against your site exactly as it ran the first time — the answer you get is the re-run's evidence, not an opinion.