Skip to main content
ScanoraAI
Audit methodology v10.0

How we audit a website

This page documents what the ScanoraAI engine actually does, signal-by-signal. Every audit draws 23 verified signals — broken down as 10 third-party APIs + 5 in-house parsers + 8 in-process probes. That math (10 + 5 + 8 = 23) is the canonical “23 verified sources” count referenced everywhere on this site, and the breakdown below lets you count it yourself. AI translates every finding into plain-language explanations, but never produces a score. Every claim here corresponds to code you can read or output you can verify yourself. If you find a discrepancy, that's a bug — please tell us.

The 23 signals — count them yourself

10 third-party APIs

  1. Google PageSpeed Insights / Lighthouse
  2. axe-core (WCAG 2.1 AA)
  3. SSL Labs
  4. Mozilla Observatory
  5. Google Safe Browsing
  6. W3C HTML Validator
  7. Website Carbon
  8. Wappalyzer tech-stack detection
  9. DNS health (Cloudflare / Google DoH)
  10. Wayback Machine (domain age)

5 in-house parsers

  1. Content-SEO parser
  2. schema.org JSON-LD validator
  3. robots.txt + sitemap.xml deep audit
  4. Image weight / format audit
  5. Internal-link graph + orphan detector

8 in-process probes

  1. JS-shell detection
  2. Rendered-meta extraction
  3. TLS certificate-chain inspection
  4. CDN detection
  5. Cookie security audit
  6. Broken link sampling
  7. Exposed file detection
  8. Mixed content detection

Items 1–10 are third-party APIs — independent public services. Items 11–15 are in-house parsers — checks we run on the page bytes we already fetched. Items 16–23 are in-process probes — checks we compute by inspecting the live page during the scan. All 23 together = the canonical "23 verified sources" count used throughout the site.

Scope note: Wappalyzer tech-stack detection runs via HTTP probe (server-side fetch). Technologies that are only loaded or revealed by a browser executing JavaScript — such as client-side analytics tags, dynamically injected frameworks, or SPA hydration signals — may not appear in results. Browser-based Wappalyzer will always detect more than a server-side probe.

Three principles

  • Only show numbers we measured. If we didn't crawl every page on the site, we don't print “7 pages are missing meta descriptions.” We say what we observed on the homepage and recommend a fuller crawl with a tool that actually does one.
  • Lab data and field data are different things. Lighthouse runs an automated test from one machine. CrUX (Chrome User Experience Report) is what real Chrome users actually experienced over the last 28 days. We label both clearly and never mix them.
  • Verifiable, not just trustworthy. Every audit links you to Google's own PageSpeed Insights tool with the same URL pre-filled, plus a download of the raw PSI response we received. You don't have to take our word for any of it.

Evidence labels — how we describe confidence

Every finding, score, and source claim in ScanoraAI maps to one of seven canonical evidence labels. The label answers a single question: how was this number produced?

We adopted this vocabulary in v8.1 (May 2026) after an audit review flagged that phrases like “verified”, “estimated”, “AI-generated”, and “never estimated” had drifted in meaning across the product. The labels are now shared across the report UI, the methodology page, and the engine's typed contracts.

Measured

Lab measurement from a provider measurement API. Reproducible — re-run and compare.

Field

Real-user data (CrUX, RUM). Sample-based — not synthetic.

Lab

Synthetic measurement under controlled throttling. Reproducible; does not reflect real users.

Observed

Deterministic parse of page bytes, headers, HTML, or structured data by our in-house parsers.

Inferred

Derived from observed signals via published rules. Documented heuristic, not measurement.

AI-explained

LLM-generated prose explaining a measured or observed finding. Never the source of a score.

Unavailable

Provider failed or signal does not apply. Shown explicitly, never silently dropped.

Implementation: src/lib/evidence-labels.ts (shared module) and src/components/EvidenceLabel.tsx (UI chip). See /self-audit for the labels applied to a real audit run.

The audit pipeline

Every audit goes through these stages, in order. Each stage has a kill-switch — if a stage fails clearly (DNS doesn't resolve, the URL serves a parking page, etc.), we stop and tell you instead of producing a bogus score.

  1. Pre-scan gate. 11 named checks: DNS resolution, TCP reachability, TLS validity, redirect loops, parked-domain detection, soft 404s, placeholder/under-construction pages (including unconfigured WordPress / Ghost / Drupal / Joomla installs), and empty shells. Failures emit audit.gate.reject with the named reason.
  2. HTTP probe. A single GET request to the homepage with a real user-agent. We extract: response time, content length, charset, response headers, the parsed DOM (heading hierarchy, image/link/form counts, schema.org JSON-LD, Open Graph tags, viewport meta, canonical, hreflang, robots), and security headers.
  3. PageSpeed Insights (Lighthouse + CrUX). 5 independent runs per strategy (desktop + mobile), fired in parallel from our server against Google’s public PSI REST endpoint. We take the median performance score across runs and flag high variance (>15 points) explicitly. Desktop scores drive the main category scoring formula; the mobile performance score is surfaced separately as mobilePerfScore and used for the mobileScore metric. CrUX field data is extracted separately when Google has enough Chrome traffic for your site.
  4. Signal evaluation. Each signal is a pure function over the probe + Lighthouse data. Penalties are deterministic — same inputs always produce the same penalty.
  5. Recommendation ranking. Issues are ranked by a transparent formula (see the Scoring section below). No language model is involved in the ranking.
  6. Persistent storage. The full report and raw PSI response are saved to a database with a 90-day retention. Every report has a SHA-256 fingerprint of (URL, timestamp, scores, signal IDs) — if anyone edits the data, the fingerprint changes.

Lighthouse and Core Web Vitals

Performance scores come from Google's PageSpeed Insights API v5, which runs the same Lighthouse engine you can run yourself at pagespeed.web.dev. We do not use a custom Lighthouse fork or modified scoring.

Run count
5 runs per strategy
Aggregation
Median score
High-variance flag
Triggers if max − min > 15
Strategies tested
Desktop + Mobile (both)
Per-run timeout
120 seconds
Verbatim response
Stored and downloadable

Lighthouse is non-deterministic — even Google's own tool returns scores that vary by ±5 points between runs on the same URL. That's why we run it 5 times and use the median, and why our scores will sometimes differ slightly from a single re-run on pagespeed.web.dev.

Desktop vs mobile scores: the main category scores (SEO, Performance, UX, Design) are driven by the desktop Lighthouse run. The mobile performance score is surfaced separately as mobilePerfScore in the report payload and shown in the audit results as your “Mobile Performance” score. Google’s ranking algorithm uses mobile Core Web Vitals — if your mobile score differs significantly from your desktop score, that gap is worth prioritising.

CrUX field data — when it’s available

The Chrome User Experience Report (CrUX) is real-user data Google publishes for sites with enough Chrome traffic. When CrUX data exists for a URL we show it explicitly and label it “p75 real users” — that's the 75th-percentile experience over the last 28 days, the standard Web Vitals threshold.

CrUX requires a minimum traffic threshold. Roughly 1 in 4 sites we audit don't meet it. When CrUX data is unavailable we show a clear “Not enough real-world traffic” panel — we never invent placeholder field values.

We also flag lab/field divergence: if Lighthouse's lab measurement is more than 20 points off the CrUX field measurement, that's a signal worth knowing about (typically the homepage was tested on a fast pipe but real users hit slower mobile networks).

Structural signals

These come from the HTTP probe — we evaluate the homepage HTML directly:

  • SEO basics: title length and uniqueness, meta description presence and length, canonical, hreflang, Open Graph, schema.org JSON-LD, robots directives.
  • Accessibility (homepage scope): language attribute, viewport meta, image alt count vs missing-alt count (real numbers — we count what's in the DOM we received).
  • Security headers: HSTS, CSP, X-Frame-Options, Referrer-Policy, Permissions-Policy. Graded the same way securityheaders.com does.
  • Performance hints: resource counts and sizes from Lighthouse, missing width/height on images, render-blocking resources, unused CSS/JS opportunities.

What we actually crawl, and what we don't: every audit deeply analyses the homepage, then up to 9 more inner pages — discovered from the most-linked internal anchors on the homepage, with a sitemap.xml fallback when the homepage is link-sparse (SPAs, hero-only landing pages). Hard cap: 10 pages total, server-enforced. The actual page count varies — content-rich sites usually hit 10, single-page apps with no sitemap audit just the one. Every result page shows the live “Audited N pages”count from your specific run. The robots/sitemap source additionally HEAD-samples up to 10 evenly-spaced URLs from the sitemap to spot broken links — a sample, not an exhaustive check. Signals that say “may be missing on inner pages” reflect this bounded scope honestly. We do not crawl every URL on a 5,000-page site. For an exhaustive full-site crawl, use Screaming Frog or Sitebulb — they do that job properly.

Full rule set — all 60+ issue categories

Every check the engine runs, its source, the maximum penalty it can apply, and its confidence level. Verified = measured directly by Lighthouse or a third-party provider. Estimated = derived from structural HTML analysis.

SEO
Rule / DescriptionSourceMax penaltyConfidence
title-missing

No <title> tag on the page.

Probe20Verified
title-too-long

Title exceeds 60 characters — truncated in SERPs.

Probe8Verified
title-too-short

Title under 10 characters — not descriptive.

Probe5Verified
meta-description-missing

No meta description tag.

Probe15Verified
meta-description-long

Meta description over 160 characters.

Probe5Verified
h1-missing

No H1 tag on the page.

Probe12Verified
multiple-h1

More than one H1 — dilutes primary keyword signal.

Probe6Verified
canonical-missing

No canonical link tag — risk of duplicate content.

Probe8Verified
canonical-self-mismatch

Canonical points to a different URL than the page.

Probe6Verified
robots-noindex

Page has noindex directive — excluded from search.

Probe25Verified
sitemap-missing

No sitemap.xml found at /sitemap.xml or in robots.txt.

Probe8Verified
robots-txt-missing

No robots.txt found.

Probe5Verified
og-tags-missing

No Open Graph tags — poor social sharing previews.

Probe6Verified
schema-missing

No JSON-LD structured data detected.

Probe5Estimated
lh-seo-score

Lighthouse SEO category score below threshold.

Lighthouse20Verified
Performance
Rule / DescriptionSourceMax penaltyConfidence
lcp-slow

LCP > 4s (poor). Largest Contentful Paint.

Lighthouse20Verified
lcp-needs-work

LCP 2.5–4s (needs improvement).

Lighthouse10Verified
fcp-slow

FCP > 3s. First Contentful Paint.

Lighthouse12Verified
tbt-high

TBT > 600ms. Total Blocking Time.

Lighthouse15Verified
cls-high

CLS > 0.25. Cumulative Layout Shift.

Lighthouse12Verified
ttfb-slow

Server response time > 800ms.

Probe10Verified
no-response-compression

No gzip/brotli Content-Encoding header.

Probe9Verified
no-cache-headers

No Cache-Control, ETag, or Last-Modified.

Probe5Verified
blocking-scripts-in-head

3+ non-async/defer scripts in <head>.

Probe10Verified
render-blocking-resources

Lighthouse detected render-blocking resources.

Lighthouse8Verified
unused-css

Significant unused CSS bytes.

Lighthouse6Verified
unused-javascript

Significant unused JavaScript bytes.

Lighthouse6Verified
unoptimised-images

Images not in modern format (WebP/AVIF) or oversized.

Lighthouse8Verified
missing-image-dimensions

Images missing width/height — causes layout shift.

Probe5Estimated
lh-performance-score

Lighthouse performance score below threshold.

Lighthouse20Verified
UX & Accessibility
Rule / DescriptionSourceMax penaltyConfidence
missing-html-lang

No lang attribute on <html>. WCAG 3.1.1 Level A.

Probe6Verified
missing-viewport-meta

No viewport meta tag — broken mobile layout.

Probe10Verified
images-missing-alt

Images without alt text (count-weighted).

Probe8Verified
lh-accessibility-score

Lighthouse accessibility score below threshold.

Lighthouse20Verified
axe-violations

WCAG 2.1 AA violations detected by axe-core.

axe-core15Verified
color-contrast

Text/background contrast ratio below 4.5:1.

Lighthouse8Verified
tap-targets-small

Touch targets smaller than 48×48px.

Lighthouse5Verified
form-labels-missing

Form inputs without associated labels.

Lighthouse6Verified
focus-visible-missing

Interactive elements lack visible focus indicator.

Lighthouse5Verified
heading-order-broken

Heading hierarchy skips levels (H1→H3).

Probe5Estimated
no-https

Site not served over HTTPS.

Probe20Verified
mixed-content

HTTPS page loads HTTP sub-resources.

Probe8Verified
Security
Rule / DescriptionSourceMax penaltyConfidence
ssl-grade-low

TLS grade below A (deprecated protocols, weak ciphers).

SSL Labs15Verified
ssl-expired

TLS certificate expired or expiring within 14 days.

SSL Labs25Verified
hsts-missing

No Strict-Transport-Security header.

Probe / Mozilla8Verified
csp-missing

No Content-Security-Policy header.

Mozilla6Verified
x-frame-missing

No X-Frame-Options header — clickjacking risk.

Mozilla5Verified
safe-browsing-threat

URL flagged in Google Safe Browsing threat lists.

Safe Browsing25Verified
mozilla-obs-grade-low

Mozilla Observatory grade below B.

Mozilla10Verified
Design & Best Practices
Rule / DescriptionSourceMax penaltyConfidence
lh-best-practices-score

Lighthouse best practices score below threshold.

Lighthouse15Verified
console-errors

JavaScript errors logged to console.

Lighthouse8Verified
deprecated-apis

Deprecated browser APIs in use.

Lighthouse5Verified
doctype-missing

No <!DOCTYPE html> declaration.

Probe5Verified
w3c-errors

HTML spec violations (duplicate IDs, unclosed tags).

W3C8Verified
carbon-high

Page CO₂ per view above 1g (not on green hosting).

Website Carbon4Verified
favicon-missing

No favicon detected.

Probe3Verified
tech-outdated

Detected CMS/framework version with known CVEs.

Tech Detector5Estimated

Penalties are additive within a category and capped at the category floor (8 points minimum). The exact penalty applied to any signal depends on severity multipliers — see the Scoring section below. This table reflects engine v10.0. Rule IDs are stable across versions; penalties may be tuned.

New signals added in engine v6.0

Engine 6.0.0 added four probe-derived signals that fire only when the probe field is explicitly populated. An undefined field means “unknown” — it never fires a false negative. All four carry confidence: 'verified' and appear in structural-only mode.

no-response-compression
Perf −9. Fires when the Content-Encoding response header is absent — the server is not gzip/brotli compressing HTML.
blocking-scripts-in-head
Perf −6 (or −10 at 6+ scripts). Fires when 3 or more non-async/non-defer <script> tags appear in <head>, blocking the parser before first paint.
missing-html-lang
UX −6. Fires when the <html> element has no lang attribute. Required by WCAG 3.1.1 Level A — screen readers need this to select the correct language engine.
no-cache-headers
Perf −5. Fires when the response has neither Cache-Control nor a validator (ETag / Last-Modified). Browsers cannot reuse the response without re-fetching.

These signals were added because they are directly observable from a single HTTP response — no Lighthouse run required — and they represent real, fixable problems that affect a large fraction of audited sites.

Third-party verified sources (engine v10.0)

Engine 7.0 adds seven independent third-party sources that run in parallel alongside the Lighthouse probe. Each source is free, has no API key requirement (except Safe Browsing which reuses your existing PSI key), and produces results you can independently verify by pasting your domain into the provider's own UI.

Mozilla Observatory
Security headers graded A+–F by Mozilla's own engine. Tests HSTS, CSP, X-Frame-Options, Referrer-Policy, Subresource Integrity, and more. Verify at developer.mozilla.org/en-US/observatory.
W3C Nu HTML Checker
The canonical HTML5 validator. Reports spec violations (duplicate IDs, unclosed tags, invalid nesting) that break screen readers and crawlers. Verify at validator.w3.org/nu.
Qualys SSL Labs
TLS configuration graded A+–F. Checks certificate expiry, deprecated protocol versions (TLS 1.0/1.1), Heartbleed, POODLE, and HSTS preload status. Cache behaviour: we always request a cached result (fromCache=on, maxAge=24h) — a fresh SSL Labs scan takes 60–180 seconds and would block the audit. If no cached result exists, we trigger a background scan and return null for this provider in the current audit (you'll see “TLS depth check pending — re-run in a few minutes”). This is distinct from our in-process TLS cert check (see below), which runs on every audit regardless. Verify at ssllabs.com/ssltest.
Google Safe Browsing
Checks the URL against Google's malware, phishing, and unwanted-software threat lists. Uses the same database Chrome uses for interstitial warnings. Reuses your PSI API key.Conditional source: the Safe Browsing API is a separate Google Cloud service that must be enabled on the same project as your PageSpeed key. If it's not enabled, every audit will show this provider as unavailable with the error HTTP 403 — and the report will display a one-click enable link. It's a free API (10,000 queries/day on the free tier) and takes about two minutes to enable.Verify at Google Transparency Report.
Website Carbon
Estimates CO₂ per page view and checks whether the host is in The Green Web Foundation's renewable-energy database. Verify at websitecarbon.com.
axe-core (Deque)
The automated WCAG 2.1 AA rule engine used by Microsoft, BBC, IBM, and Salesforce. Runs on the static DOM via jsdom and surfaces issues automation can catch — by Deque's own published numbers, that's roughly 57% of WCAG violation types. Important limitations of the jsdom context: CSS is not computed, so colour-contrast violations are not detected; JavaScript-rendered content and dynamic state changes are not evaluated; focus management and keyboard navigation cannot be tested. The Lighthouse accessibility audit (which runs in a real Chrome headless context) catches contrast and some dynamic issues — both results are combined in your report. Manual review is still required for full accessibility conformance (keyboard traversal, focus order, screen-reader semantics, and cognitive accessibility cannot be fully automated). Each violation cites the axe rule ID and WCAG criterion. Verify rules at dequeuniversity.com/rules/axe.

Engine v8.0 added eight more in-process and lightweight sources that run on every audit:

TLS Cert Health (in-process)
Fast in-process TLS handshake — checks certificate expiry (warns if <30 days), self-signed certs, hostname mismatch, and mixed-content signals. Runs on every HTTPS site regardless of SSL Labs cache status. Distinct from SSL Labs: this check is fast (<2s) and always present; SSL Labs provides the deeper cipher-suite and protocol-version grade when cached.
DNS Health (Cloudflare DoH)
SPF, DMARC, CAA, AAAA, and MX record checks via Cloudflare's DNS-over-HTTPS resolver. Flags missing SPF/DMARC (email spoofing risk), absent CAA (certificate issuance not restricted), and IPv6 readiness. No API key required.
Image Audit (in-process)
Pure HTML analysis — flags images missing width/height attributes (CLS risk), lazy-load coverage, modern-format opportunity (WebP/AVIF vs JPEG/PNG), andfetchpriority misuse on LCP images. No network request — runs on the HTML we already fetched.
Wayback Machine (domain age)
Queries the Internet Archive CDX API for the first-seen date of the domain. Used as a trust signal — a domain first seen last week is treated differently from one with a 10-year history. Silent no-op for un-archived hosts.
Content SEO (in-process)
Regex-based analysis of the fetched HTML — title length, meta description, heading hierarchy, keyword density signals, and internal link structure. Under 50ms, no network request.
Schema.org Validator (in-process)
Parses JSON-LD blocks and checks required properties for the detected schema type. Flags missing @type, empty required fields, and malformed JSON. Under 50ms, no network request.

18 schema types with full required-property validation:

ArticleAggregateRatingBlogPostingBreadcrumbListEventFAQPageHowToLocalBusinessNewsArticleOfferOrganizationPersonProductQuestionRecipeReviewVideoObjectWebSite

Schema types not in this list produce an informational signal with zero score deduction and a link to Google Rich Results Test — we never silently skip or claim full coverage.

Robots / Sitemap Deep Audit
Fetches and parses robots.txt and sitemap.xml. HEAD-samples up to 10 evenly-spaced sitemap URLs to spot broken links. Flags disallow-all, missing sitemap, and sitemap/robots mismatches. ~2–5s with sampling.
WordPress Stack Health
WordPress-only (silent no-op on non-WP sites). Detects WordPress core version, public plugin slugs, and theme slugs from the served HTML, then queries the official wordpress.org APIs to check whether each is current. Only reports on what is provably detectable from the HTML — inactive plugins, premium plugins not listed on wordpress.org, and security-obscured installs will not appear. Confidence is “verified” when both a detected version and a wordpress.org response are available; “inferred” when the slug is known but the version could not be confirmed.

Engine v10.0 adds eight in-process probes (JS-shell detection, rendered-meta extraction, TLS certificate-chain inspection, CDN detection, cookie security audit, broken link sampling, exposed file detection, mixed content detection) that produce findings the sources above cannot. Each is its own evidence source: it has distinct inputs, distinct outputs, and surfaces failures the other 15 sources miss (10 third-party + 5 in-house parsers, listed above).

JS-Shell Detection (in-process)
What it measures: the gap between what a non-JS HTTP crawler sees and what a full-browser renderer sees. We fetch the raw HTML with no JavaScript execution and measure indexable text density, structural elements, and heading presence in the un-rendered response. We then compare against the post-hydration DOM Lighthouse already inspected.

Distinct from Lighthouse: Lighthouse executes JavaScript in headless Chrome and scores the rendered DOM — so a single-page app that hydrates to rich content will score well on Lighthouse SEO. The shell problem is exactly what Lighthouse resolves; it's not a signal Lighthouse surfaces. The signal we surface is the divergence: a site that scores 95 on Lighthouse SEO can still ship 2 KB of empty <div id="app"> to crawlers. That matters because Google's initial crawl, most LLM training crawlers, social-preview generators, and many SEO tools index the raw response — not the rendered DOM — especially for new pages that haven't entered the rendering queue yet.

Example finding: “Raw HTML contains 47 chars of indexable text and zero <h1>. Lighthouse SEO score is 95 because the rendered DOM is rich. Crawlers that don't execute JS will index this page as empty until Google's render queue catches up — which can take days to weeks for low-priority URLs.”
Rendered Meta Extraction (in-process)
What it measures: meta tags injected by client-side head managers (react-helmet, next/head, vue-meta) that exist in the rendered DOM but not in the SSR response HTML. We diff the <head> of the raw HTTP response against the <head> of the post-hydration DOM and flag divergences in title, meta[name=description], link[rel=canonical], and Open Graph tags.

Distinct from Lighthouse + content-SEO: Lighthouse reports only the rendered <head> — it sees a populated title and gives you a green check. Our in-process content-SEO source reads only the raw HTML — it sees a default <title>React App</title> and flags a missing title. Neither one alone tells you the real story: that the title exists, just not in the SSR payload Googlebot may or may not re-render depending on crawl budget. This source is the only one that surfaces the gap explicitly.

Example finding: “Rendered DOM title is 'About — Acme' (set by react-helmet). Raw SSR response title is 'React App' (default Vite template). Social-preview crawlers, LLM training crawlers, and Google's initial crawl pass will see the wrong title.”
TLS Cert Chain (in-process)
What it measures: the full certificate chain returned during the TLS handshake — leaf cert SAN coverage, intermediate cert presence and order, exact expiry date, and OCSP staple status. Surfaces chain-incomplete and intermediate-misconfigured cases that produce “works in Chrome but fails in curl/Java/Android” bug reports.

Distinct from SSL Labs and TLS Cert Health: SSL Labs returns a letter grade and is API-rate-limited (one scan per few hours per host). TLS Cert Health (above) is a fast handshake that checks expiry and self-signed status but does not parse intermediate certs. This source parses the full DER-encoded chain Node's TLS getPeerCertificate(detailed) returns and runs chain-completeness rules SSL Labs grades but doesn't expose as machine-readable signals.

Example finding: “Leaf cert valid, but intermediate cert is missing from the chain. Browsers with cached intermediates accept the connection; clients without (curl, older Java, some mobile webviews) reject with UNABLE_TO_VERIFY_LEAF _SIGNATURE.”

Source-count breakdown (18 total):

  • 10 third-party providers — PageSpeed Insights, axe-core, SSL Labs, Mozilla Observatory, Safe Browsing, W3C Validator, Carbon API, Wappalyzer, Cloudflare DoH (DNS Health), Wayback Machine
  • 5 in-house parsers on fetched HTML — Content SEO, Schema.org Validator, Robots/Sitemap, Image Audit, Internal Link Graph
  • 3 in-process network/runtime probes — JS-Shell Detection, Rendered Meta Extraction, TLS Cert Chain (the three documented above)

We use “source” to mean any check that produces findings no other check on this list produces. By that definition every entry above is a source. If you prefer the stricter framing “independent API call to an external provider,” the count is 10 — and the other 8 are documented in-house parsers and probes that run on every audit. Both framings are honest; we picked the broader one because the evidence behaviour is what actually drives the report.

Honesty contract: if a provider call fails (network error, rate limit, cache miss), we return null for that provider — never “passed”. The report shows which providers ran and which were unavailable. Provider signals carry confidence: 'verified' only when the upstream call succeeded and returned a populated field.

Desktop vs mobile Lighthouse (engine v8.2)

Both desktop and mobile Lighthouse strategies run on every audit (5 runs each, in parallel). The desktop run drives the main category scores (SEO, Performance, UX, Design) and the overall score. The mobile performance score is surfaced separately as mobilePerfScore in the report and shown in the audit results UI.

Overall = mean(SEO, Design, UX, Performance)   ← all from desktop Lighthouse

Mobile Performance score = separate field (mobilePerfScore)
                         ← from mobile Lighthouse 5-run median

Rationale: desktop scores are more stable (lower variance) and are what most site owners compare against their own PageSpeed Insights runs. The mobile score is surfaced prominently because Google's ranking algorithm uses mobile Core Web Vitals — a large gap between your desktop and mobile scores is a high-priority finding.

Why not weight mobile ×1.6 in the overall? The previous methodology documented a 1.6× mobile weighting. This was removed in v8.2 because it produced an overall score that diverged from what users see when they run PageSpeed Insights themselves (which shows desktop and mobile separately). Showing both scores explicitly is more honest than blending them into a single weighted number that neither matches.

AI per-issue explanations (engine v8.3)

For each Critical and Medium finding, the engine generates a 2–4 sentence explanation that:

  • References your actual numbers. The prompt receives the Lighthouse JSON for that specific finding — LCP element, image size, blocking time. The model is forbidden from inventing metrics.
  • Names the specific element. “Your hero image banner.jpg is 3.8 MB and is your LCP element” — not “large images slow your page.”
  • Gives the stack-specific fix. The tech detector identifies your CMS/framework first. The explanation then gives the exact fix for your stack: WordPress plugin, Shopify theme setting, Next.js component, etc.
  • Labeled as AI-generated. Every explanation carries an aiGenerated: true flag and cites the model used. It is an enhancement to the verified finding — it never replaces the score deduction or the upstream source citation.

Grounding check: before storing an explanation, we verify it references at least one number or technology name from the input context. Generic explanations that don't reference your specific data are discarded and the original finding is kept unchanged.

Multi-page site audit

The multi-page audit runs the full single-page pipeline on up to 10 pages of a site and aggregates the results. It does not replace a full-site crawler — it is a bounded, honest sample.

  • Page discovery. We first audit the homepage, then extract up to 9 additional URLs from the homepage's internal links (most-linked = most important). If the homepage has fewer than 3 internal links (SPA shell, hero-only landing page), we fall back to sitemap.xml.
  • Sequential, not parallel. Pages are audited one at a time with a 3-second politeness delay between requests. Parallel audits would exhaust the PSI key pool (4 keys × 5 runs × 10 pages = 200 simultaneous calls).
  • Failure tolerance. If a page fails (timeout, blocked, error), we record the failure and continue. We never silently drop a page or pretend it succeeded.
  • Aggregation. We report the arithmetic mean of per-page scores, the worst page (priority fix target), the best page (template to replicate), and signals that appear on 3+ pages (template/CMS issues — fix once, fix everywhere).

Honesty contract: we report exactly how many pages were audited, not “the whole site.” We never extrapolate from 10 pages to 10,000. The UI shows a discovery note explaining which pages were found and how.

Scoring and recommendation ranking

Each signal carries a penalty (0–25 points) and lands in a category: SEO, Performance, UX/Accessibility, or Design. Each category starts at 100. The probe and Lighthouse layers populate real findings on top, deducting points per active signal. The final per-category score blends Lighthouse (when available) with the structural signals at the rates below, then clamps to [8, 96]:

Performance       = 70% Lighthouse perf      + 30% structural
SEO               = 70% Lighthouse SEO       + 30% structural
UX/Accessibility  = 70% Lighthouse a11y      + 30% structural
Design            = 50% Lighthouse practices + 50% structural

Overall           = mean(perf, SEO, UX, design)   ← desktop Lighthouse
Mobile perf score = separate field (mobilePerfScore) ← mobile Lighthouse

Quick-win bonus   = +2 priority for fixes ≤ 5 min

When Lighthouse fails or is unavailable for the audit, the affected categories carry an “unverified” flag in the report payload (dataSources.<cat>.verified = false) and the UI shows a clearly-labelled notice in place of fabricated numbers. We do not synthesise category scores from a URL hash — that was removed in engine 6.0.0.

Recommendations are ranked by:

priority = penalty × severityMultiplier × confidenceBoost ÷ effortMultiplier

The multiplier values are fixed constants — not tuned per-run or per-model:

severityMultiplier
  critical  → 2.0
  high      → 1.5
  medium    → 1.0
  low       → 0.6
  info      → 0.3

confidenceBoost
  verified  (Lighthouse / axe-core measured)  → 1.2
  estimated (structural inference)            → 1.0
  inferred  (heuristic / single-source)       → 0.8

effortMultiplier
  trivial   (< 5 min,  e.g. add alt text)    → 0.5   ← quick-win boost
  low       (< 30 min, e.g. add meta tag)    → 0.8
  medium    (< 2 h,   e.g. compress images)  → 1.0
  high      (< 1 day, e.g. lazy-load JS)     → 1.4
  complex   (> 1 day, e.g. CLS layout fix)   → 2.0

All four factors are deterministic — same inputs always produce the same priority order. We do not use an LLM to rank issues; the AI chat is purely an explanatory layer over the deterministic findings.

Worked example — “Missing alt text on 14 images”:

Signal:   missing-alt-text
Penalty:  12 points
Severity: high      → severityMultiplier = 1.5
Source:   axe-core  → confidenceBoost    = 1.2  (verified)
Effort:   trivial   → effortMultiplier   = 0.5  (< 5 min)

priority = 12 × 1.5 × 1.2 ÷ 0.5
         = 12 × 1.5 = 18
         = 18 × 1.2 = 21.6
         = 21.6 ÷ 0.5 = 43.2  ← ranked #1 in most audits

Compare: "Render-blocking JS" (penalty 18, high, verified, high effort)
priority = 18 × 1.5 × 1.2 ÷ 1.4 = 23.1  ← ranked lower despite higher penalty

The trivial effort divisor (0.5) intentionally surfaces quick wins above harder fixes of equal impact — because a fix you can ship in 5 minutes delivers value faster than one that takes a day.

Reliability layer

PSI sometimes blips. We handle it without lying about the data:

  • Multi-key pool with round-robin rotation across up to 4 PSI keys. Per-key cooldown after 429/403.
  • Circuit breaker opens after 5 consecutive PSI failures, with a 30s cooldown and a single half-open probe before fully closing.
  • Targeted retry on 429 / 5xx / ECONNRESET / ETIMEDOUT only. Three attempts with exponential backoff and full jitter. Never retries on AbortError or 4xx (other than 429).
  • Honest fallback: when Lighthouse is unavailable for a URL, we show a clear panel explaining why and never fabricate Core Web Vitals numbers. Structural signals still apply.

Security & abuse controls

ScanoraAI is a public URL scanner. That means we must prevent it from being used as a proxy for SSRF attacks, internal network probing, or denial-of-service amplification. Here is what we enforce:

  • Private IP blocking. Before any outbound HTTP request, the target hostname is resolved and the resulting IP is checked against RFC 1918 / RFC 4193 private ranges (10.x, 172.16–31.x, 192.168.x, fc00::/7, ::1, 127.x, 169.254.x). Requests to private IPs are rejected with a 400 before any connection is made.
  • DNS rebinding protection. The IP check is performed after DNS resolution, not on the hostname string, so a domain that resolves to a private IP is blocked even if the hostname looks public.
  • Protocol allowlist. Only https:// and http:// URLs are accepted. File, FTP, data, and other schemes are rejected at input validation.
  • Rate limiting. Single-page audits are limited to 5 per IP per 10 minutes. Site audits (multi-page crawl) are limited to 2 per IP per 10 minutes. Limits are enforced server-side before any external requests are made.
  • Timeout caps. Each outbound source has a hard timeout (PSI: 120 s, SSL Labs: 150 s, all others: 30 s). The overall audit has a 12-minute watchdog that terminates any hanging job and returns partial results.
  • Redirect following. HTTP redirects are followed up to a maximum of 5 hops. Each hop is re-validated against the private IP blocklist so a redirect chain cannot escape to an internal address.
  • No JavaScript execution. Our probe fetches raw HTTP responses — it does not execute JavaScript, render a DOM, or run any user-supplied code. This eliminates the XSS-via-audit-target attack surface.
  • Responsible disclosure. If you find a security issue, email admin@scanoraai.com with subject line “Security disclosure”. We aim to respond within 48 hours.

How to verify our numbers

Three independent ways:

  1. Re-run on Google directly. Every audit page has a button that opens pagespeed.web.dev with your URL pre-filled. Compare the scores. They will be highly similar (accounting for normal run-to-run variance).
  2. Download the raw PSI response. Each audit links to the verbatim JSON Google returned to us, unmodified. You can run curl against the same endpoint with your own API key and diff the responses.
  3. Check the audit fingerprint. Every report displays a 16-character SHA-256 prefix at the top. If any data in the report changes after the audit was generated, the fingerprint won't match. We compute it from (url, scannedAt, category scores, signal IDs).
  4. Read our self-audit. We run the engine on scanoraai.com itself and publish the full output — scores, source timestamps, findings, and AI explanations — at /self-audit. We don't hide our own issues. The homepage currently scores 72 on mobile performance due to a large JS bundle — that's a known issue we are actively fixing.

Benchmarks & accuracy (roadmap)

Saying we are accurate is not the same as proving it. We are committing in public to publishing measurable accuracy data — false-positive and false-negative rates against a fixed corpus of test sites — on a quarterly cadence.

What that means in practice:

  • A fixed benchmark corpus. We are assembling a public set of test sites with known, deliberately-injected issues — broken canonicals, missing alt text, mixed-content warnings, slow LCP, low-contrast text, malformed JSON-LD — plus a control set of clean sites. Every release we run the engine against the corpus and record which signals fired correctly, which were missed, and which fired in error.
  • Published metrics. For each major signal, we will publish precision (no false positives), recall (no false negatives), and the corpus version it was tested against. Numbers will appear on this page and on /transparency.
  • Quarterly cadence. The first benchmark report is targeted for Q3 2026. After that, every quarter, alongside the engine changelog. We will publish regressions as well as improvements — if a new release worsens recall on a signal, that will be visible.
  • External corroboration. Where a signal is measured by an external tool (Lighthouse, axe-core, SSL Labs, W3C Validator), we already link out to the provider so anyone can cross-check us. Benchmarks add the missing piece: statistically validated agreement, not just spot checks.

This section is intentionally a roadmap commitment, not a finished number. The audit engine currently passes a 644-test internal suite covering signal detection, scoring math, and provider failure handling. The benchmark corpus is the next layer up — testing the engine as a black box against ground-truth issues. We'd rather under-promise here than publish cherry-picked numbers.

Known limits

  • The single-page audit covers the homepage only. Inner-page issues are inferred from homepage signals where appropriate. The multi-page audit extends this to up to 10 pages — see the Multi-page audit section above.
  • We don't test logged-in or paywalled pages.
  • CrUX data has a ~28-day publication lag and a minimum traffic threshold; for low-traffic sites it's unavailable.
  • Lighthouse runs from Google's test infrastructure. Sites that block Googlebot or restrict by IP will fail PSI but may still be audited via structural signals only.
  • We do not (and cannot) audit JavaScript-rendered content the way a real Chromium-based crawler would. PSI does, which is one reason we lean on it for performance.
  • SSL Labs latency: SSL Labs' API performs a full TLS handshake analysis and can take 60–120 seconds for a domain it hasn't recently cached. It runs in parallel with other sources, but on a fresh domain it is typically the last source to complete and is the most common reason an audit approaches the 5-minute ceiling. Cached results (domains scanned within ~24 hours) return in under 5 seconds.

Engine changelog

Every material change to scoring, signals, data sources, or honesty disclosures is recorded here. We don't silently retune weights — if a check changes how it deducts points, or a new data source goes live, it shows up in this list. Versions follow the audit engine (separate from the marketing-site version).

v8.3May 2026JS-framework rendering gap closed: engine now detects React/Next.js/Nuxt/Vue/Gatsby/Remix/SvelteKit shells and overrides raw-HTML-derived signals with Lighthouse-rendered values.
  • New signal

    JS-shell detection (engine v8.3): the probe now identifies JS-framework sites (Next.js, Nuxt, Gatsby, Remix, SvelteKit, Create React App, Angular) by inspecting the raw HTML for framework-specific markers. When a shell is detected, jsRendered=true is set on the probe.

  • New signal

    Rendered meta extraction (engine v8.3): Lighthouse/PSI runs a full Chromium render and exposes post-JS-execution signals via its SEO and accessibility audits (document-title, meta-description, canonical, viewport, is-crawlable, hreflang, structured-data, image-alt, html-has-lang, tap-targets). These are now extracted as renderedMeta on the LighthouseMetrics object.

  • New signal

    Rendered override (engine v8.3): when jsRendered=true and PSI renderedMeta is available, the engine overrides raw-HTML-derived signals (pageTitle, metaDescription, hasCanonical, hasOgTags, hasSchema, hasViewportMeta, robotsNoindex, hasAltTextOnAllImages) with the Lighthouse-rendered values. This closes the gap where React/Next.js/Nuxt sites were incorrectly flagged for missing meta tags that were actually present after JS execution.

  • Honesty fix

    No new dependencies required: the rendered signals come from the PSI/Lighthouse API that already runs on every audit. Zero additional latency. The jsRendered flag is surfaced in the audit report so users can see when their site was detected as a JS framework.

v8.3May 2026Mobile Lighthouse run added; source count corrected to 18; axe-core jsdom limitations disclosed; SSL Labs cache behaviour documented; WP Stack Health scope clarified.
  • New signal

    Mobile Lighthouse run (engine v8.2): both desktop and mobile strategies now run on every audit (5 runs each, in parallel). Desktop drives the main category scores for consistency with what users see in PageSpeed Insights. Mobile performance score is surfaced separately as mobilePerfScore in the report payload and shown in the audit results UI. Closes the gap where a user could get a high desktop score but a poor mobile score without being told.

  • Honesty fix

    Source count corrected from 15 to 18: the orchestrator runs 17 providers (techDetector, Wappalyzer, WP Stack Health, CrUX, TLS cert, DNS health, image audit, Wayback, content-SEO, schema validator, robots/sitemap, Safe Browsing, Mozilla Observatory, W3C, SSL Labs, Website Carbon, axe-core) plus PSI/Lighthouse = 18 total. All marketing copy updated site-wide.

  • Honesty fix

    axe-core jsdom limitations disclosed on methodology page: CSS not computed (colour-contrast violations not detected), JavaScript-rendered content not evaluated, focus management not testable. Lighthouse accessibility audit (real Chrome headless) noted as the complement that catches contrast and dynamic issues.

  • Honesty fix

    SSL Labs cache behaviour documented: always uses fromCache=on&maxAge=24h. Cache miss triggers a background scan and returns null for this provider in the current audit. Distinct from in-process TLS cert health check (which always runs).

  • Honesty fix

    WordPress Stack Health scope clarified: only reports on what is provably detectable from served HTML. Inactive plugins, premium plugins not on wordpress.org, and security-obscured installs will not appear. Confidence levels (verified vs inferred) documented.

v8.1May 2026Credibility hardening: 7-label evidence vocabulary, Lighthouse run-variance display, root-cause grouping, ranking/privacy/WCAG wording cleanup.
  • Honesty fix

    Evidence-label vocabulary: every finding, score, and source claim now maps to one of 7 canonical categories — measured / field / lab / observed / inferred / ai_explained / unavailable. Replaces ad-hoc phrases like "verified" / "estimated" / "AI-generated" / "never estimated" that the v8.1 audit review flagged as overclaim-prone. Shared module at src/lib/evidence-labels.ts; reusable EvidenceLabel UI chip; visible on /sample-report and across audit results.

  • New signal

    Lighthouse run-variance display: median scores now ship with a 5-run distribution band (min, max, spread, confidence: high / medium / low). A 14-point spread on mobile = medium confidence; the report tells you to re-run on PageSpeed before optimising. Hides nothing — synthetic noise is real and now visible.

  • New signal

    Root-cause grouping: shared upstream issues are linked across findings. The "oversized JavaScript" group, for example, drives LCP, TBT, mobile performance, transfer size, and carbon. Lead finding contributes full penalty; followers contribute reduced share to avoid double-counting. Surfaced in /sample-report and in audit results.

  • Honesty fix

    Ranking-claim cleanup: removed the "same Lighthouse engine Google uses for ranking" phrasing. New canonical wording: "Lighthouse scores are diagnostic; Core Web Vitals from CrUX field data are the actual ranking signal — page experience can influence search performance." Lighthouse is a tool, not a ranking signal.

  • Honesty fix

    axe-core / WCAG cleanup: every reference to automated accessibility checks now states "Manual review is still required for full accessibility conformance." axe-core catches roughly 57% of WCAG violation types automatically (Deque's published number); the rest require human review of keyboard traversal, focus order, screen-reader semantics, and cognitive accessibility.

  • Honesty fix

    Privacy-wording cleanup: "Zero data retention" / "URL never stored" removed. New canonical statement: "No account required for public scans. Reports retained 90 days if you create one. Raw provider payloads stored only for verification then deleted. No PII collected, no tracking cookies, no data sold or shared." Resolves the contradiction between zero-retention claims and 90-day report storage.

  • Honesty fix

    Source-count unification: every page now uses the canonical phrase "18 evidence sources: 10 external providers + 5 in-house parsers." Replaces drift across "7 verified sources", "8 sources", "10 third-party providers", and "18 verified sources" that the audit review flagged.

  • Bug fix

    Source-count drift sweep: site-wide audit found four UI components still hard-coding stale counts after the canonical phrase was introduced — TrustLogosStrip, WhyScanoraSection, the audit-results header, and the sample-report SOURCES table. All four now read from a single SOURCE_COUNT_PHRASE constant in src/lib/evidence-labels.ts so future count changes happen in one place. Verified by grep across pages/ and components/ and a final smoke test against ma.tt + news.ycombinator.com (17 providers wired, 30 + 35 signals, dataSources.verified across all 5 categories).

v8.0May 2026Stack-aware fix library, carbon context layer, /transparency honesty fix, CrUX absence visibility.
  • Honesty fix

    Stack-aware fix badges: verified-fix blocks now show "WordPress-specific" / "Next.js-specific" (indigo) or "Generic HTML" (slate) badges with hover tooltips explaining which variant was selected and why. Previously a tiny "Stack: X" chip was too quiet to signal tailoring.

  • Honesty fix

    Carbon context layer: raw grams of CO₂ per page view are now paired with (1) Website Carbon's global-average anchor (0.36 g/view, ~50 M-site dataset), (2) a per-URL trend vs. stored history (null when no prior reading exists — no fabricated context), and (3) a deterministic severity-graded one-line recommendation keyed off cleanerThan percentile and green-host status.

  • Honesty fix

    /transparency 0/0/0/0/0 bug: when no self-audit file existed, the page rendered literal zero ScoreBars that read as "we scored 0/100 across the board." Fixed via a pending: true discriminator — the API now returns pending when no real data exists, and the page renders an explicit "First self-audit pending" panel instead.

  • Honesty fix

    CrUX absence: FieldLabToggle now always renders an explicit notice when loadingExperience.available === false, explaining the 28-day Chrome opted-in traffic threshold. Previously the toggle silently showed nothing.

  • New signal

    carbon-context: new /private/carbon-history/ store (per-URL, capped at 20 entries) feeds the "vs. your past audits" trend line. Isolated from the existing score-history store so the audit-history API is unaffected.

v7.0Early 2026Third-party source expansion to 11 data sources, AI honesty stack, pre-verified fix library, schema validator honesty.
  • Source added

    Expanded from 8 to 18 data sources: PSI/Lighthouse (4-key rotation + circuit breaker + 5-run parallel median/variance), axe-core, Mozilla Observatory v2, SSL Labs, Safe Browsing, W3C Validator, Website Carbon, Wappalyzer, plus 3 in-house providers: content-SEO, schema validator, robots/sitemap deep audit.

  • New signal

    Pre-verified fix library: 25 top signals × 4 stacks (WordPress, Next.js, Shopify, generic) → canonical exactFix blocks. UI shows verified-fix block below AI prose. Fixes are human-curated, not LLM-generated.

  • Honesty fix

    AI honesty stack: constrained JSON output schema rejects empty fields and ungrounded citations. Gemini 2.5 Flash primary, GPT-4o Mini fallback. AI prose is clearly labelled as AI-generated; verified-fix blocks are labelled as human-curated.

  • Honesty fix

    Schema validator honesty: exports SUPPORTED_SCHEMA_TYPES (18 types). Unsupported schema types produce a single grouped schema-unsupported-types Info signal with scoreDeduction: 0 and a pointer to Google Rich Results Test, rather than silently failing or claiming full coverage.

  • New signal

    Trust UI: ScoreDriversPanel, ScoreStabilityPanel, FieldLabToggle, ThirdPartySourcesPanel, raw PSI route, /methodology v8.3 badge. Per-category dataSources provenance surfaced in every audit result.

  • New signal

    ConfidenceLabel gains UNVERIFIED tier. Providers map internal Info severity to Low at the boundary so the Severity union stays clean: Critical | Medium | Low | Pass.

v6.5Late 2025Site-audit mode, robots/sitemap deep audit, content-SEO provider, two-mode architecture.
  • New signal

    Site-audit mode (/api/site-audit): bounded crawl ≤10 pages, 3 s politeness delay, 12-min watchdog, 2 audits/IP/10 min rate limit. Separate from single-page mode (/api/audit).

  • New signal

    Robots/sitemap deep audit: hand-rolled XML parser (<50 ms on 50 k URLs), auto-descends sitemapindex, HEAD-samples up to 10 URLs to verify reachability.

  • New signal

    Content-SEO provider: thin content detection, keyword-in-H1/H2, heading hierarchy, anchor-text quality, multiple-H1 check.

  • New signal

    Schema.org validator: @graph descent, multi-type arrays, required-property table for 18 schema types.

v6.0Mid 2025First public engine version with stable signal IDs, PSI key rotation, and the honesty-first scoring contract.
  • New signal

    Stable signal-ID contract established. Signal IDs are now permanent public identifiers — changes to an existing signal's behaviour are recorded in this changelog rather than silently applied.

  • Source added

    PSI/Lighthouse integration with 4-key rotation and circuit breaker. Mobile performance weighted higher than desktop to reflect real-world user distribution.

  • Source added

    Mozilla Observatory v2, SSL Labs, Google Safe Browsing, W3C Validator, Website Carbon, Wappalyzer added as third-party sources. All results attributed to their source in the UI.

  • Honesty fix

    scoreDeduction: 0 allowed for Info-level signals. Transparency signals (e.g. carbon footprint, schema coverage) can appear in results without affecting the score, so users see the full picture without being penalised for things outside their control.

  • Honesty fix

    Auth rate-limiting (5/min/IP per action) and audit rate-limiting established. Sign-out exempted from rate limits.

Earlier internal iterations (pre-v6) are not listed because they predate the public commitment to a stable signal contract. v6.0 is the first version whose signal IDs and penalty caps match what runs in production today.

Machine-readable methodology

The full audit methodology is also published as JSON at /.well-known/audit-methodology.json for tools that want to verify our claims programmatically.

Run an audit

Your competitors are outranking you right now.

Free AI audit in seconds — Lighthouse + 23 verified sources, AI-ranked fix list, no account, no card.

+30 pt score lift guaranteed or we redo it free · managed rebuild only

Audit My Site Free →
Newsletter

Audit tips, SEO insights & product updates

No spam. Unsubscribe anytime. One email per week, max.

ScanoraAI

Scanora AI is an AI-powered website intelligence platform. Instant Lighthouse audits, AI-ranked fix plans, and optional managed rebuilds — no developer required.

Real-time Lighthouse analysisNo signup required — everFree audit, alwaysOne-time project pricingSatisfaction guaranteed (managed rebuilds)

Product

© 2026 Scanora AI. All rights reserved.

AI engine · online · fast audits

·

Smarter Audits. Faster Fixes.

·

Powered by Scanora AI