Every check, and the rule behind it
An audit takes 355 measurements of a website, and 60 rules decide which of them become a finding. Each one is listed below with the exact threshold, what measured it and how confident the reading is. This page is generated from the code that applies the rules, so it cannot describe a check we do not run.
When a check cannot be measured
A site that is down, slow to answer or behind a bot challenge has not shown us anything about itself. We report that check as not measured, leave it out of the score, and say why. It is never a pass and never a fail.
When a fix is checked again
Checks marked "rechecked on request" can be measured again on their own after a fix, by the same rule and to the same standard of evidence as the audit that found them. Checks fed by a business listing or reviews update with the next audit instead.
The same failed measurement, read two ways
In our October 2026 review of 38 audit tools, one graded a well-known, healthy website 0 out of 100 for performance and called it essentially non-functional. The likeliest cause is the tool's own request failing, not the site. This is what each approach tells the reader.
A tool that grades whatever loaded
0/100
"Performance: essentially non-functional." The reader is told their site is broken, and an agency that forwards this report loses the client the first time they open their own website.
SiteAssay
Not measured
"The performance check did not complete, because the site did not answer our measurement in time. It is left out of the score." The rest of the report stands, and nothing in it is a guess.
Speed
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
Time to First Byte How quickly the server sends the first byte of the page. Under 800ms feels instant; past 2.5s mobile visitors start leaving. speed.slow_server_response |
Raised when the reading is above 2,500 ms. | Live HTTP timing · CDN/cache headers · Google PageSpeed (Lighthouse) Rechecked on request | high |
|
Largest Contentful Paint When the main content becomes visible. Google wants this under 2.5s; it is a direct ranking and conversion factor. speed.slow_largest_paint |
Raised when the reading is above 4,000 ms. | Live HTTP timing · CDN/cache headers · Google PageSpeed (Lighthouse) Rechecked on request | high |
|
Text Compression Whether HTML/CSS/JS is compressed (gzip/brotli) before download. Uncompressed pages are far larger and slower on mobile. speed.no_compression |
Raised when the reading is none. | Live HTTP timing · CDN/cache headers · Google PageSpeed (Lighthouse) Rechecked on request | high |
|
Cumulative Layout Shift How much the page visually jumps while loading. Lower is steadier; above 0.25 feels broken and misclicks rise. speed.layout_shift |
Raised when the reading is above 0.25. | Live HTTP timing · CDN/cache headers · Google PageSpeed (Lighthouse) Rechecked on request | high |
Search
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
Web design + hosting presence.no_website |
Raised when the business has no website of its own. | Business listing | high |
|
Indexable (no noindex) Whether the homepage carries a robots "noindex" directive, which tells search engines to drop it from results entirely. indexing.homepage_noindex |
Raised when the check detects it. | Homepage HTML · sitemap/robots probes · structured-data parse Rechecked on request | high |
|
Local SEO rankings.not_ranking |
Raised when the reading is 0. | Google Business Profile and reviews | high |
|
Schema Markup Structured data that lets Google show rich results (star ratings, FAQs, business info). Competitors with it get the eye-catching listings. schema.missing |
Raised when the check looks for it and does not find it. | Homepage HTML · sitemap/robots probes · structured-data parse Rechecked on request | high |
|
XML Sitemap A sitemap.xml helps search engines discover and index every page. Without it, pages can be missed. indexing.no_sitemap |
Raised when the check looks for it and does not find it. | Homepage HTML · sitemap/robots probes · structured-data parse Rechecked on request | high |
|
Markup Not on the Page Names, phone numbers and addresses stated in the structured data that the visible page does not show. Google requires markup to describe what a visitor can see. schema.content_mismatch |
Raised when the reading is above 0. | Homepage HTML · sitemap/robots probes · structured-data parse Rechecked on request | medium |
|
Broken Links Links on the site that lead to a page that does not answer or answers with an error. links.broken |
Raised when the reading is above 4. | lychee Rechecked on request | high |
|
Structured Data That Fails To Parse Whether the page carries JSON-LD structured data that is not valid JSON. Blank when the page has none, which is a separate finding. schema.unparseable |
Raised when the check detects it. | Homepage HTML · sitemap/robots probes · structured-data parse Rechecked on request | high |
|
LocalBusiness Markup Whether the homepage carries LocalBusiness structured data, or a local type such as Dentist or Plumber. schema.missing_local_business |
Raised when the check looks for it and does not find it. | The homepage, read for local business markup · compared with the Google listing Rechecked on request | high |
|
Technical SEO schema.retired_rich_result |
Raised when the reading is above 0. | schema Rechecked on request | high |
Security
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
Exposed Secret Files Count of publicly reachable sensitive files (.env, .git, phpinfo, open admin/debug dashboards). Any is a critical breach risk, because credentials and source code are downloadable. exposure.sensitive_files |
Raised when the reading is above 0. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Secrets in JavaScript Count of private API keys (Stripe, AWS, Supabase, tokens) hardcoded into the shipped JavaScript. Any is critical, because the keys are downloadable by anyone. exposure.leaked_api_keys |
Raised when the reading is above 0. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Supabase Database Open Whether the Supabase database returns real rows to the public anon key shipped in the app, meaning row-level security is off. Any exposed table is a critical breach. exposure.supabase_rls_off |
Raised when the check detects it. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Valid HTTPS Certificate Whether the SSL/TLS certificate is present and valid. Without it browsers show a full-page "not secure" warning. tls.invalid_certificate |
Raised when the check looks for it and does not find it. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Backend Rules Missing Whether a Firebase or Convex backend answered an anonymous request with data, meaning its security rules are absent or wide open. exposure.backend_rules_open |
Raised when the check detects it. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Blacklist Status Whether the site is on a malware/phishing blacklist. A listing means Google warns visitors away and traffic collapses. blocklist.malware |
Raised when the check detects it. | Google Business rating & review volume · malware/phishing blacklists Rechecked on request | high |
|
Certificate Expiry Days until the HTTPS certificate expires. When it lapses, every visitor is blocked by a security warning. tls.expiring_soon |
Raised when the reading is below 30 days. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Known Vulnerabilities Count of known security issues detected. Each is an open door for a hack, defacement or data breach. scanner.known_vulnerabilities |
Raised when the reading is above 0. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Mail Blocklist Status Whether the domain's mail-sending IP is on an email spam blocklist. A listing means the business's own emails bounce or land in spam. blocklist.mail_ip |
Raised when the check detects it. | Google Business rating & review volume · malware/phishing blacklists Rechecked on request | high |
|
Vulnerable JavaScript Libraries Known published vulnerabilities in the JavaScript libraries the site ships. stack.vulnerable_js_library |
Raised when the reading is above 0. | js_cve Rechecked on request | high |
|
Debug Mode Exposed Whether error pages leak full stack traces (APP_DEBUG left on), revealing file paths, config and database internals to any visitor. exposure.debug_mode |
Raised when the check detects it. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Forced HTTPS Whether plain http:// is redirected to https://. Without it, visitors can land on an insecure version and see warnings. tls.no_https_redirect |
Raised when the check looks for it and does not find it. | RDAP/WHOIS · DNS records (A/MX/NS, DNSSEC) · redirect & archive checks Rechecked on request | high |
|
Mixed (Insecure) Content Number of insecure http:// resources loaded on a secure page. Browsers block these and flag the page as not fully secure. tls.mixed_content |
Raised when the reading is above 0. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
DMARC Record Tells receivers what to do with mail that fails SPF/DKIM, the key control that stops others impersonating the domain. email.no_dmarc |
Raised when the check looks for it and does not find it. | DNS TXT records (SPF, DKIM, DMARC, BIMI) · MX lookup Rechecked on request | high |
|
Hosting IP Reputation Whether the site's hosting IP is flagged for malicious activity in threat intelligence. A flag means bad-neighbour hosting that erodes trust. blocklist.hosting_ip |
Raised when the check detects it. | Google Business rating & review volume · malware/phishing blacklists Rechecked on request | high |
|
Permissive CORS Whether the site lets any origin make credentialed cross-origin requests, allowing other websites to read logged-in users' data. headers.permissive_cors |
Raised when the check detects it. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Security Headers Grade How well HTTP response headers protect against clickjacking, XSS and downgrade attacks. F means the protections are missing. headers.grade_f |
Raised when the reading is F. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
SPF Record Declares which servers may send email for the domain. Without it, mail is easier to spoof and more likely to hit spam. email.no_spf |
Raised when the check looks for it and does not find it. | DNS TXT records (SPF, DKIM, DMARC, BIMI) · MX lookup Rechecked on request | high |
|
GraphQL Introspection Whether the GraphQL API exposes its full schema via introspection, handing attackers a complete map of the data model and available operations. exposure.graphql_introspection |
Raised when the check detects it. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
|
Insecure Cookies Cookies missing Secure/HttpOnly flags, which is a session-hijacking and privacy-compliance risk. cookies.insecure_flags |
Raised when the reading is above 0. | TLS handshake · HTTP security headers · vulnerability, exposure & blacklist checks Rechecked on request | high |
Accessibility
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
WCAG Errors Number of automated WCAG 2.2 AA failures found by axe-core (contrast, labels, alt text, target size). Each excludes some users and adds ADA/EAA legal exposure. a11y.wcag_failures |
Raised when the reading is above 9. | Automated WCAG 2.2 AA checks (axe-core) · alt-text coverage Rechecked on request | medium |
|
Image Alt-text Coverage Percentage of images with descriptive alt text, which helps image SEO and makes the site usable with screen readers. images.missing_alt |
Raised when the reading is below 80%. | Automated WCAG 2.2 AA checks (axe-core) · alt-text coverage Rechecked on request | high |
Technology
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
Domain Expiry Days until the domain registration lapses. If it expires, the website and email go dark instantly. domain.expiring_soon |
Raised when the reading is below 45 days. | RDAP/WHOIS · DNS records (A/MX/NS, DNSSEC) · redirect & archive checks Rechecked on request | high |
|
Up-to-date Software Whether the platform version is current. Outdated software is the most common way small-business sites get hacked. stack.outdated_cms |
Raised when the check detects it. | HTTP fingerprinting · CDN/WAF & mail-provider detection Rechecked on request | medium |
|
Mail (MX) Records Whether the domain can receive email. No MX means the business cannot get mail at its own domain. email.no_mx |
Raised when the check looks for it and does not find it. | RDAP/WHOIS · DNS records (A/MX/NS, DNSSEC) · redirect & archive checks Rechecked on request | high |
Reputation
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
Google Rating Average star rating on Google. Buyers filter out anything under 4.0, so a low rating directly costs calls. reviews.low_rating |
Raised when the reading is below 4. | Google Business rating & review volume · malware/phishing blacklists | high |
|
Negative Recent Reviews The share of recent Google reviews that are one or two stars. reviews.mostly_negative |
Raised when the reading is above 30%. | Google Business Profile and reviews | high |
|
Review Count How many Google reviews the business has. Too few is a weak trust signal versus more-reviewed competitors. reviews.too_few |
Raised when the reading is below 20. | Google Business rating & review volume · malware/phishing blacklists | high |
|
Reviews Replied To Share of recent reviews the business has publicly answered. reviews.unanswered |
Raised when the reading is below 20%. | Google reviews captured for this business | high |
|
Days Since Last Review How long ago the newest Google review was left. Shown only when every review on the listing was captured, because from a partial sample the newest one captured may not be the newest there is. reviews.stale |
Raised when the reading is above 180 days. | Google reviews captured for this business | high |
AI visibility
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
AI Crawlers Allowed Whether robots.txt lets the major AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended) read the site at all. robots.blocks_ai_crawlers |
Raised when the check looks for it and does not find it. | Structured-data parse · answerable-content heuristics · llms.txt probe Rechecked on request | medium |
|
Answerable Content Whether the content is structured as clear questions and answers that ChatGPT, Perplexity and Google AI Overviews can cite. answers.not_answerable |
Raised when the check looks for it and does not find it. | Structured-data parse · answerable-content heuristics · llms.txt probe Rechecked on request | medium |
|
Business Entity Defined Whether Organization or LocalBusiness schema clearly identifies the business as an entity AI engines can recognise. schema.missing_entity |
Raised when the check looks for it and does not find it. | Structured-data parse · answerable-content heuristics · llms.txt probe Rechecked on request | medium |
Turning visits into calls
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
Clear Call-to-Action Whether there is an obvious next step (Book, Call, Get a Quote). Without one, interested visitors leave instead of converting. conversion.no_cta |
Raised when the check looks for it and does not find it. | Homepage content analysis (CTAs, forms, trust signals, mobile UX) Rechecked on request | medium |
|
Lead-capture Form Whether the site can collect an enquiry. No form means a ready-to-buy visitor has no way to leave their details. conversion.no_lead_form |
Raised when the check looks for it and does not find it. | Homepage content analysis (CTAs, forms, trust signals, mobile UX) Rechecked on request | medium |
|
Mobile-friendly Whether the page is built to render usably on phones, which is critical when most local traffic is mobile. mobile.no_viewport |
Raised when the check looks for it and does not find it. | Homepage content analysis (CTAs, forms, trust signals, mobile UX) Rechecked on request | medium |
|
Tap-to-Call Link Whether the phone number is a tappable link. On mobile, most callers will not copy a number by hand. conversion.no_click_to_call |
Raised when the check looks for it and does not find it. | Homepage content analysis (CTAs, forms, trust signals, mobile UX) Rechecked on request | medium |
|
Forms That Submit Nowhere Forms whose action is "#" or a javascript: placeholder, with no submit handler declared in the markup. forms.posts_nowhere |
Raised when the reading is above 0. | Homepage content analysis (CTAs, forms, trust signals, mobile UX) Rechecked on request | medium |
|
Reviews / Testimonials Whether social proof is shown on the page. Visible reviews reassure first-time buyers and lift conversion. conversion.no_trust_signals |
Raised when the check looks for it and does not find it. | Homepage content analysis (CTAs, forms, trust signals, mobile UX) Rechecked on request | medium |
|
Buttons That Go Nowhere Call-to-action links on the page whose target is empty, "#" or a javascript: placeholder, with no click handler declared in the markup. conversion.dead_cta |
Raised when the reading is above 0. | Homepage content analysis (CTAs, forms, trust signals, mobile UX) Rechecked on request | medium |
|
Submit Buttons Outside a Form Submit buttons not inside a form and not linked to one with a form attribute. forms.orphan_submit_button |
Raised when the reading is above 0. | Homepage content analysis (CTAs, forms, trust signals, mobile UX) Rechecked on request | medium |
|
Map On The Website Whether the homepage embeds a Google map. local.no_map_embed |
Raised when the check looks for it and does not find it. | The homepage, read for local business markup · compared with the Google listing Rechecked on request | high |
Content quality
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
Thin Homepage Content Whether the homepage carries too little text for a search engine or an AI assistant to understand what the business does. content.thin_homepage |
Raised when the check detects it. | seo Rechecked on request | high |
|
JavaScript Console Errors Number of runtime JavaScript errors and failed requests the browser logged while loading the page. quality.console_errors |
Raised when the reading is above 0. | Headless-browser probe (console, coverage, hydration) · homepage placeholder scan Rechecked on request | medium |
Google Business Profile
| Finding | Rule | Evidence | Confidence |
|---|---|---|---|
|
Website Matches The Google Listing Whether the name, phone and address on the website agree with the Google listing. False on any disagreement; blank when neither side published enough to compare. nap.mismatch |
Raised when the check looks for it and does not find it. | The homepage, read for local business markup · compared with the Google listing | high |
|
Map Share Of Voice The share of Local Grid points around the business where it appears in the map results at all, from the latest completed grid. maps.low_share_of_voice |
Raised when the reading is below 25%. | The business listing captured during discovery | high |