How the health score works

View Markdown

Every crawl produces a score out of 100 and a letter grade. This document explains how it is computed and, more importantly, what it is and is not claiming.

The research and citations behind the current model are in scoring-recalibration.md.


The grade bands

ScoreGradeVerdict
90–100AExcellent
80–89BGood
70–79CNeeds Work
60–69DPoor
below 60FCritical

The verdict word is composition-aware: a low score with no critical findings is not described as "Critical", because calling a site critical when nothing critical was found misrepresents it.

Calibration target: a competently built site with the ordinary imperfections every real site has — some long meta descriptions, imperfect heading order, a few redirects — should land in the high B range, not be taxed down into a C or D. A site scoring in the 50s should have something genuinely wrong with it.


The model: pages, not findings

The score is built on a page-defect backbone rather than a count of issues. This is the single most important thing to understand about it, because it is what makes the number stable.

Counting findings does not work. A 5,000-page site with one templated flaw generates 5,000 findings and scores zero; a 10-page site with three catastrophic problems generates 3 and scores well. Both answers are wrong. So each check is classified by what it does to the page:

ImpactMeaningWeight
PageBrokenGoogle drops or refuses to index the page100
PageDefectiveThe page is materially defective for ranking55
SiteHighA real site- or link-level fault, but the page is not broken12 (capped at 24)
SiteMediumSecondary technical problems4 (capped at 14)
MinorReal but small1 (capped at 12)
UnscoredReported, never scored0

A broken page costs its full share of the site. That matches the one published industry formula — Ahrefs' health score tracks the share of pages without errors — so a site where 20% of pages 404 loses roughly 20 points from that term alone, which is the intuition most SEOs already have.

Why the caps exist

Every real site has some long meta descriptions and some imperfect heading order. Without a collective cap, those near-universal findings accumulate until a well-built site scores like a broken one. Capping each tier's total contribution is what keeps the minor stuff visible in the report without letting it dominate the number.

Proportion, with two guards

Page-level deductions are proportional to the share of the site's pages affected, counting each page once however many times a check names it — 15 pages missing a description means something different on a 15-page site than on a 1,000-page one. Two guards keep that honest at the extremes:

  • A coverage floor (0.12) so a single instance still registers on a large site rather than rounding to nothing.
  • A minimum denominator (5 pages) so one bad page on a three-page crawl is not scored as "100% of the site is broken".

What is deliberately not scored

Security headers do not affect the score. Missing HSTS or CSP is a real finding and Raptor reports it, but it is a security concern, not a ranking factor, and letting it move an SEO score would misrepresent both.

AI-crawler blocking does not affect the score. Blocking GPTBot or setting noai is a business decision. Raptor reports it without an opinion.

Findings on non-indexable pages are mostly gated off. A page excluded from the index cannot cause a duplicate-title problem. The duplicate checks also leave out pages canonicalised to another URL, so a filter or tracking variant that names its base page as canonical is not scored as a duplicate of that page.

Deliberate states are Notes (scoring model 3). These nine checks are still reported, but they never cost points, because they usually describe a choice the owner made on purpose or a part of the site's template:

  • directives.noindex, directives.nofollow, directives.none, directives.nosnippet and directives.noimageindex: pages that ask search engines not to index them, follow their links, show a snippet or index their images;
  • canonicals.canonicalised: pages whose canonical names another page;
  • canonicals.canonical_mismatch: pages whose canonical is declared differently in the HTML and the HTTP header;
  • response_codes.internal_redirect: internal links that pass through one redirect;
  • headings.h2_duplicate: pages that repeat an H2 used on another page. The repeats are often the template's own section headings, such as "Frequently asked questions", so change one only if it is meant to be unique to the page.

Under scoring model 2 they cost points as Minor findings, so a site with three intentional noindex pages was told to undo them. Crawls stored before model 3 keep their stored score.


Priority and ranking

Every finding has a priority, which comes from its impact:

ImpactPriority
PageBrokenFix now
PageDefective, SiteHighHigh
SiteMediumMedium
MinorLow
UnscoredNote

The report, the terminal and MCP all list findings in one order:

Findings are ranked by priority first: every Fix now finding comes before every High one, then Medium, then Low, with Notes last. Within a priority, the finding that would win back more health points if fixed comes first, where points are measured by scoring the crawl again without that finding. Ties go to the finding that reaches more pages, then to the order of Raptor's check catalogue.

Points are what a finding costs. A finding's points are the deduction it adds: the crawl is scored again without it, before the score is limited to the range 0 to 100. When deductions pass 100 the score stops at 0, so fixing one finding alone may not lift it; the report says so. A finding in a capped tier can cost nothing on its own while the tier is over its cap; the report shows it as part of that capped tier rather than as "+0.0". Points are shown only when the current scoring model explains the stored score.


When the score is withheld

A crawl that reached no pages, or was blocked, does not receive a grade. Raptor refuses to score rather than invent a number from an empty crawl — a 0-page crawl rendering a confident all-green chart was a real defect, and the fix was to say "unscorable" instead.

A crawl that saw nothing readable is withheld too, with the verdict "No readable content — crawl blocked or client-rendered". That happens when nearly every page is thin and either has no links or is byte-identical to other pages. Byte-identical is measured on the pages themselves, whatever their canonicals say: an app shell whose every route names / as its canonical reports no duplicates, but it is still one empty page served everywhere, and it is not graded.


How to read it

The score is a triage signal for one site over time, not a benchmark between sites. It answers "is this site healthier than it was last month, and where should I look first?" It does not answer "is this site better than a competitor", because it has no access to backlinks, rankings, traffic, intent or content quality — the things that actually decide competitive outcomes.

Treat the category breakdown as the real output and the number as the headline. Two sites can score 83 for entirely different reasons, and the reasons are what you act on.


Counting units

The crawl summary counts problems in three different units. They answer different questions, so read the one that matches yours:

  • summary.total_issues, critical_issues, warning_issues and info_issues count URL rows. A site-wide finding counts 1, and each URL appears once per check.
  • findings_count counts the checks that fired, and findings_by_priority splits that count by priority.
  • pages_with_findings counts distinct crawled pages, after following redirects.
  • In MCP, raptor_query's affected_page_count counts the distinct URLs a finding lists, as written. The report model's page count follows redirects to the crawled page, so two URLs that are one page count once.
  • scoring_model is the analysis-and-scoring model version. It is 3 from this release (model 2 scored deliberate states as Minor findings); crawls without the field were model 1. 0 means the summary was never scored.
  • ranking_model is the version of the ranking rule above. 0 means the crawl was never ranked.