
Website Auditor
Scoring Guide
The complete reference for how our free website auditor evaluates your site across 12 dimensions with 540 field-level signals and checks. The headline is a field-signal count, not a count of independent or passing tests. Version 5.2 adds 19 passive coverage checks; review findings do not deduct points.
All scores computed from HTML source, HTTP headers, and robots.txt — no external APIs, no AI inference.
Unfamiliar with a metric below? Browse our glossary of 290+ terms
The current edition, explained · v5.2
Reference updated October 9, 2026. The total describes the auditor’s capacity, not the number of tests executed or passed on one page.
471 fields + 50 extension checks + 19 coverage checks = 540
Think of the catalog as the toolbox and each report as one inspection. Fields include observations, estimates and contextual data. Checks interpret that evidence. An unavailable input does not mean a test passed.
The catalog is rebuilt from the implementation on every build. A new field or check identifier increases the total; reusing a field does not count it twice. Editing a recommendation does not add a metric. A partial report can no longer reduce the public total.
The coverage checks return pass, fail, review or not applicable. A failure deducts two points in its dimension; review and not applicable deduct none. Content-substance caps and the weighted average are applied afterward.
A different canonical can consolidate equivalent content. Noindex can be intentional. llms.txt is optional; SearchAction earns no additional points. Performance estimates are not field measurements, and HTML analysis does not certify accessibility, security or indexing.
Download the guide and complete metrics catalog (PDF)How Scoring Works
Each dimension starts at 100 points. Points are deducted for missing elements, poor implementations, and security gaps. Thin or empty content then receives a substance-based cap, preventing missing content from earning an artificially strong result. The composite is the weighted average of the capped dimensions.

Current source-derived deductions — 84 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Deduct 20 points
Missing title tag
When: NOT (p.titleLength >= 50 && p.titleLength <= 60) AND (p.titleLength === 0)
Deduct 5 points
Title tag is short ([observed value] chars)
When: NOT (p.titleLength >= 50 && p.titleLength <= 60) AND NOT (p.titleLength === 0) AND (p.titleLength < 50 && p.titleLength >= 30)
Deduct 10 points
Title tag is very short ([observed value] chars)
When: NOT (p.titleLength >= 50 && p.titleLength <= 60) AND NOT (p.titleLength === 0) AND NOT (p.titleLength < 50 && p.titleLength >= 30) AND (p.titleLength < 30)
Deduct 5 points
Title tag is too long ([observed value] chars — may be truncated in SERPs)
When: NOT (p.titleLength >= 50 && p.titleLength <= 60) AND NOT (p.titleLength === 0) AND NOT (p.titleLength < 50 && p.titleLength >= 30) AND NOT (p.titleLength < 30)
Deduct 8 points
Title tag contains a duplicated brand suffix (e.g., "My Page | Brand | Brand") — wastes title space and looks unprofessional in SERPs
When: (p.titleBrandSuffixDuplicated)
Deduct 4 points
Cloudflare Email Protection detected ([observed value] obfuscated links) — these appear as /cdn-cgi/l/email-protection and return 404 to crawlers
When: (p.cfEmailObfuscationDetected)
Deduct 6 points
[observed value] suspicious internal link pattern(s) detected: [observed value]
When: (p.internalBrokenLinkPatterns.length > 0)
Deduct 15 points
Missing meta description
When: NOT (p.metaDescLength >= 140 && p.metaDescLength <= 160) AND (p.metaDescLength === 0)
Deduct 3 points
Meta description is slightly short ([observed value] chars)
When: NOT (p.metaDescLength >= 140 && p.metaDescLength <= 160) AND NOT (p.metaDescLength === 0) AND (p.metaDescLength >= 120 && p.metaDescLength < 140)
Deduct 7 points
Meta description is short ([observed value] chars)
When: NOT (p.metaDescLength >= 140 && p.metaDescLength <= 160) AND NOT (p.metaDescLength === 0) AND NOT (p.metaDescLength >= 120 && p.metaDescLength < 140) AND (p.metaDescLength < 120)
Deduct 4 points
Meta description may be truncated ([observed value] chars)
When: NOT (p.metaDescLength >= 140 && p.metaDescLength <= 160) AND NOT (p.metaDescLength === 0) AND NOT (p.metaDescLength >= 120 && p.metaDescLength < 140) AND NOT (p.metaDescLength < 120)
Deduct 15 points
No H1 heading found
When: NOT (p.h1Count === 1) AND (p.h1Count === 0)
Deduct 8 points
Multiple H1 tags found ([observed value])
When: NOT (p.h1Count === 1) AND NOT (p.h1Count === 0)
Deduct 5 points
Heading hierarchy gaps: [observed value]
When: NOT (p.headingGaps.length === 0 && p.headingCount > 2) AND (p.headingGaps.length > 0)
Deduct 5 points
Very few headings for content length
When: (p.headingCount < 3 && p.wordCount > 300)
Deduct 8 points
Missing canonical URL
When: NOT (p.hasCanonical && p.canonicalMatchesUrl) AND (!p.hasCanonical)
Deduct 8 points
No Open Graph tags found
When: NOT (p.hasOpenGraph && p.ogMissing.length === 0) AND (!p.hasOpenGraph)
Deduct 4 points
Missing OG tags: [observed value]
When: NOT (p.hasOpenGraph && p.ogMissing.length === 0) AND NOT (!p.hasOpenGraph) AND (p.ogMissing.length > 0)
Deduct 5 points
No Twitter Card tags found
When: NOT (p.hasTwitterCard && p.twitterMissing.length === 0) AND (!p.hasTwitterCard)
Deduct 3 points
Missing Twitter tags: [observed value]
When: NOT (p.hasTwitterCard && p.twitterMissing.length === 0) AND NOT (!p.hasTwitterCard) AND (p.twitterMissing.length > 0)
Deduct 5 points
Missing lang attribute on <html>
When: NOT (p.hasLangAttr)
Deduct 3 points
Missing favicon
When: NOT (p.hasFavicon)
Deduct 4 points
[observed value] empty/unlabeled links found
When: (p.emptyLinks > 3)
Deduct 3 points
[observed value] broken anchor links detected
When: (p.brokenAnchors > 0)
Deduct 8 points
Thin content ([observed value] words)
When: NOT (p.wordCount >= 300 && p.wordCount <= 3000) AND (p.wordCount < 300)
Deduct 4 points
Deprecated HTML tags found: [observed value]
When: (p.deprecatedTags.length > 0)
Deduct 10 points
Page is set to noindex — search engines will not index this page
When: (p.hasRobotsMeta && p.robotsContent.includes('noindex'))
Deduct 5 points
Meta refresh redirect detected — harmful for SEO
When: (p.metaRefresh)
Deduct 8 points
Excessive links on page ([observed value]) — search engines may devalue link equity
When: (p.linkCount > 300)
Deduct 4 points
High link count ([observed value]) — above recommended range
When: NOT (p.linkCount > 300) AND (p.linkCount > 200)
Deduct 5 points
URL is very long ([observed value] chars) — may be truncated in SERPs and harder to share
When: (p.urlLength > 100)
Deduct 3 points
URL is longer than recommended ([observed value] chars)
When: NOT (p.urlLength > 100) AND (p.urlLength > 75)
Deduct 6 points
[observed value] external links missing rel="nofollow noopener noreferrer" — potential link equity leakage
When: (p.externalLinksWithoutRel > 10)
Deduct 3 points
[observed value] external links missing recommended rel attributes
When: NOT (p.externalLinksWithoutRel > 10) AND (p.externalLinksWithoutRel > 3)
Deduct 5 points
Hreflang tags found on a non-canonical page — search engines may ignore them
When: (p.hreflangOnNonCanonical)
Deduct 6 points
Long redirect chain ([observed value] hops) — slows crawling and dilutes PageRank
When: (p.redirectChainLength > 3)
Deduct 3 points
Redirect chain with [observed value] hops
When: NOT (p.redirectChainLength > 3) AND (p.redirectChainLength > 1)
Deduct 3 points
Title and description are identical to Open Graph tags — missed optimization opportunity
When: (p.duplicateTitleAndOg && p.duplicateDescAndOg)
Deduct 6 points
SPA framework detected ([observed value]) with thin initial HTML content — search engines may not index all content
When: (p.spaDetected && p.wordCount < 200)
Deduct 5 points
Low internal link ratio ([observed value]%) — most links point externally
When: (p.internalLinkRatio < 20 && p.linkCount > 10)
Deduct 4 points
Low anchor text diversity ([observed value]%) — many internal links use identical text
When: (p.anchorTextDiversity < 30 && p.internalLinks > 10)
Deduct 6 points
Multiple canonical tags found — search engines may ignore all of them
When: (p.multipleCanonicals)
Deduct 3 points
Title and H1 share no keywords — misaligned topic signals
When: NOT (p.titleH1Overlap > 0.5 && p.titleH1Overlap < 1) AND (p.titleH1Overlap === 0 && p.h1Count > 0 && p.titleLength > 0)
Deduct 5 points
All [observed value] external links are nofollow — no outbound link authority signals
When: (p.externalLinks > 0) AND NOT (p.dofollowExternalLinks >= 2 && p.dofollowExternalLinks <= 5) AND (p.dofollowExternalLinks === 0 && p.externalLinks > 3)
Deduct 2 points
Only 1 dofollow external link — consider adding a few more to authoritative sources
When: (p.externalLinks > 0) AND NOT (p.dofollowExternalLinks >= 2 && p.dofollowExternalLinks <= 5) AND NOT (p.dofollowExternalLinks === 0 && p.externalLinks > 3) AND (p.dofollowExternalLinks === 1)
Deduct 5 points
High dofollow external link count ([observed value]) — excessive link equity leakage
When: (p.externalLinks > 0) AND NOT (p.dofollowExternalLinks >= 2 && p.dofollowExternalLinks <= 5) AND NOT (p.dofollowExternalLinks === 0 && p.externalLinks > 3) AND NOT (p.dofollowExternalLinks === 1) AND (p.dofollowExternalLinks > 10)
Deduct 2 points
[observed value] dofollow external links — slightly above optimal range
When: (p.externalLinks > 0) AND NOT (p.dofollowExternalLinks >= 2 && p.dofollowExternalLinks <= 5) AND NOT (p.dofollowExternalLinks === 0 && p.externalLinks > 3) AND NOT (p.dofollowExternalLinks === 1) AND NOT (p.dofollowExternalLinks > 10) AND (p.dofollowExternalLinks > 5)
Deduct 4 points
Primary keyword "[observed value]" not found in H1
When: (p.primaryKeyword && p.h1Count > 0) AND NOT (p.h1ContainsKeyword)
Deduct 3 points
Primary keyword missing from first paragraph
When: (p.primaryKeyword && p.wordCount > 100) AND NOT (p.firstParaContainsKeyword)
Deduct 5 points
Weak E-E-A-T signals ([observed value] indicators)
When: NOT (p.eatSignalCount >= 5) AND NOT (p.eatSignalCount >= 3)
Deduct 2 points
Conditional deduction; see the exact trigger.
When: (!p.hasAboutPage)
Deduct 2 points
Conditional deduction; see the exact trigger.
When: (!p.hasContactPage)
Deduct 2 points
Moderate readability ([observed value] Flesch) — content may be difficult for some readers
When: (p.fleschReadingEase > 0 && p.wordCount > 100) AND NOT (p.fleschReadingEase >= 60) AND (p.fleschReadingEase >= 40)
Deduct 4 points
Low readability score ([observed value] Flesch) — content is hard to read
When: (p.fleschReadingEase > 0 && p.wordCount > 100) AND NOT (p.fleschReadingEase >= 60) AND NOT (p.fleschReadingEase >= 40)
Deduct 2 points
Missing og:locale tag — social platforms may guess language/region
When: (p.hasOpenGraph) AND (!p.ogHasLocale)
Deduct 1 points
Conditional deduction; see the exact trigger.
When: (p.hasOpenGraph) AND (p.ogImageUrl) AND NOT (p.ogImageIsWebp)
Deduct 1 points
Conditional deduction; see the exact trigger.
When: (p.hasOpenGraph) AND (p.ogImageUrl) AND NOT (p.ogImageDimensionHint === 'optimal') AND (p.ogImageDimensionHint === 'acceptable')
Deduct 3 points
OG image dimensions too small for social platforms
When: (p.hasOpenGraph) AND (p.ogImageUrl) AND NOT (p.ogImageDimensionHint === 'optimal') AND NOT (p.ogImageDimensionHint === 'acceptable') AND (p.ogImageDimensionHint === 'too_small')
Deduct 1 points
Conditional deduction; see the exact trigger.
When: (p.hasOpenGraph) AND (p.hasArticleSchema || p.wordCount > 500) AND NOT (p.articlePublishedTime)
Deduct 1 points
Conditional deduction; see the exact trigger.
When: (p.hasOpenGraph) AND (p.hasArticleSchema || p.wordCount > 500) AND NOT (p.articleModifiedTime)
Deduct 1 points
Conditional deduction; see the exact trigger.
When: (p.hasTwitterCard) AND NOT (p.twitterCardType === 'summary_large_image') AND (p.twitterCardType === 'summary')
Deduct 2 points
Twitter card type not specified
When: (p.hasTwitterCard) AND NOT (p.twitterCardType === 'summary_large_image') AND NOT (p.twitterCardType === 'summary') AND (!p.twitterCardType)
Deduct 3 points
Low internal link density ([observed value]/1k words) — below optimal 2–5 range
When: (p.wordCount >= 300) AND NOT (density >= 2 && density <= 5) AND (density < 2 && density > 0)
Deduct 5 points
No internal links in content body
When: (p.wordCount >= 300) AND NOT (density >= 2 && density <= 5) AND NOT (density < 2 && density > 0) AND (density === 0)
Deduct 3 points
Very high internal link density ([observed value]/1k words) — may appear spammy
When: (p.wordCount >= 300) AND NOT (density >= 2 && density <= 5) AND NOT (density < 2 && density > 0) AND NOT (density === 0) AND (density > 50)
Deduct 8 points
Keyword stuffing detected: "[observed value]" appears [observed value] times ([observed value]% density — exceeds 3%)
When: (p.primaryKeyword && p.wordCount >= 100) AND (p.isKeywordStuffed)
Deduct 2 points
Low keyword density ([observed value]%) for "[observed value]"
When: (p.primaryKeyword && p.wordCount >= 100) AND NOT (p.isKeywordStuffed) AND NOT (p.keywordDensityPercent >= 0.5 && p.keywordDensityPercent <= 2.5) AND (p.keywordDensityPercent > 0 && p.keywordDensityPercent < 0.5)
Deduct 3 points
Primary keyword "[observed value]" not found in body content
When: (p.primaryKeyword && p.wordCount >= 100) AND NOT (p.isKeywordStuffed) AND NOT (p.keywordDensityPercent >= 0.5 && p.keywordDensityPercent <= 2.5) AND NOT (p.keywordDensityPercent > 0 && p.keywordDensityPercent < 0.5) AND (p.keywordOccurrences === 0)
Deduct 2 points
Moderate passive voice ([observed value]%)
When: (p.wordCount >= 200 && p.passiveVoiceRatio > 0) AND NOT (p.passiveVoiceRatio <= 15) AND (p.passiveVoiceRatio <= 30)
Deduct 4 points
High passive voice ratio ([observed value]%) — harder for AI/voice extraction
When: (p.wordCount >= 200 && p.passiveVoiceRatio > 0) AND NOT (p.passiveVoiceRatio <= 15) AND NOT (p.passiveVoiceRatio <= 30)
Deduct 3 points
Long content without Table of Contents
When: NOT (p.hasTableOfContents && p.wordCount >= 800) AND (!p.hasTableOfContents && p.wordCount >= 1500)
Deduct 2 points
Conditional deduction; see the exact trigger.
When: NOT (p.zeroClickOptimized) AND (p.wordCount >= 500)
Deduct 2 points
Conditional deduction; see the exact trigger.
When: NOT (p.entitySalienceScore >= 50) AND NOT (p.entitySalienceScore >= 20) AND (p.wordCount >= 500)
Deduct 2 points
Conditional deduction; see the exact trigger.
When: NOT (p.bingeworthySignals >= 3) AND (p.bingeworthySignals === 0 && p.wordCount >= 500)
Deduct 3 points
No social sharing buttons/widgets detected
When: NOT (p.hasSocialShareButtons)
Deduct 1 points
Conditional deduction; see the exact trigger.
When: NOT (p.ogSharingCompleteness >= 100) AND (p.ogSharingCompleteness >= 67)
Deduct 3 points
Low OG sharing completeness ([observed value]%) — social previews will be poor
When: NOT (p.ogSharingCompleteness >= 100) AND NOT (p.ogSharingCompleteness >= 67) AND (p.ogSharingCompleteness < 67)
Deduct 6 points
Multiple <title> tags detected ([observed value]) — search engines may use an unintended title
When: (p.multipleTitleTags)
Deduct 6 points
Multiple meta descriptions detected ([observed value]) — engines may ignore them or pick the wrong one
When: (p.multipleMetaDescriptions)
Deduct 3 points
[observed value] internal link(s) use rel="nofollow" — this wastes internal link equity
When: (p.nofollowInternalLinks > 0)
Deduct 4 points
Conflicting character-encoding declarations detected — can cause garbled text and indexing issues
When: (p.conflictingCharacterEncoding)
Deduct 8 points
Legacy frames/framesets detected ([observed value]) — content inside frames is poorly indexed
When: (p.hasFrames)
Deduct 5 points
[observed value] image(s) have a missing or empty src — broken images hurt UX and image search
When: (p.brokenImages > 0)
Deduct 2 points
Inline CSS validation hints: [observed value]
When: (p.cssValidationHints.length > 0)
Title Tag Presence & Length
The <title> element is the single most important on-page SEO signal. Google displays it in SERPs and uses it for ranking. Optimal length is 50–60 characters (Google 2025–2026 recommendation); shorter titles waste ranking potential, longer titles get truncated.
Meta Description Presence & Length
Meta descriptions generate the snippet text in SERPs. A missing or poorly-sized description (optimal: 120–160 chars) leads Google to auto-generate snippets, often yielding lower click-through rates.
Meta Keywords Tag
The legacy engine records this field and applies a small internal rubric deduction when missing. Google does not use meta keywords for web search ranking; this internal score is not a Google ranking rule.
H1 Heading (Count & Content)
Exactly one H1 per page is best practice. Multiple H1s dilute topical focus; a missing H1 removes the primary heading signal. We also check whether the H1 contains your primary keyword (h1ContainsKeyword).
Heading Hierarchy (H2–H6)
We parse the full heading hierarchy (h2Count, h3Count, headingCount) and detect heading gaps — skipping from H2 to H4, for example, breaks semantic structure and confuses crawlers.
Canonical Tag & Consistency
Missing or multiple canonical annotations are scored. A different canonical URL can intentionally consolidate equivalent content and receives no mismatch penalty. Distinct pagination normally needs its own canonical.
Open Graph Tags (Completeness)
We audit all OG tags: og:title, og:description, og:image, og:type, og:url, og:locale. Each missing tag (ogMissing) reduces social sharing effectiveness. We also check ogHasLocale, ogImageIsWebp, ogImageDimensionHint, and whether og duplicates the title/description (duplicateTitleAndOg, duplicateDescAndOg).
Twitter Card Tags
Separate from OG, Twitter cards require twitter:card, twitter:title, twitter:description, twitter:image. We verify twitterCardType (summary_large_image is optimal) and flag twitterMissing tags.
Social Sharing Completeness
We calculate ogSharingCompleteness (0–100) combining OG + Twitter Card coverage. We also detect social share widgets (hasSocialShareButtons, socialShareWidgets) that amplify content distribution.
Image Alt Text Coverage
Every image needs descriptive alt text. We count imagesWithoutAlt and imagesWithEmptyAlt separately — empty alt="" is valid only for decorative images. Missing alt text loses both SEO image ranking and accessibility.
Internal Link Architecture
We measure internalLinks, internalLinkRatio, internalLinkDensityPer1k (links per 1,000 words), and anchorTextDiversity. A ratio below 0.5 or density below 2 per 1k words signals poor internal linking.
External Link Quality
We count externalLinks, externalLinksWithoutRel (missing noopener/noreferrer), and analyze the dofollowExternalLinks vs. nofollowExternalLinks ratio (externalLinkDofollowRatio). Excessive dofollow outbound links dilute PageRank.
Broken & Empty Links
brokenAnchors (href="#" or empty href) and emptyLinks waste crawl budget and create dead-end user experiences. We also detect internalBrokenLinkPatterns for systematic link rot.
URL Length & Structure
URLs over 75 characters are harder to share and may be truncated in SERPs. We measure urlLength and check keywordInUrl for topical relevance.
Keyword Targeting & Density
We identify the primaryKeyword, measure keywordDensityPercent, keywordOccurrences, and detect isKeywordStuffed (>3% density). We also verify h1ContainsKeyword and firstParaContainsKeyword for topical reinforcement.
Content Depth & Word Count
wordCount, paragraphCount, and avgWordsPerParagraph measure content substance. Pages under 300 words are flagged as thinContentSignal. We also check titleH1Overlap — high overlap suggests lazy optimization.
Readability & Writing Quality
fleschReadingEase measures text complexity (60–70 is ideal for web). passiveVoiceRatio above 20% reduces engagement. contentReadabilityHints provide specific improvement suggestions.
E-E-A-T Signals
We count eatSignalCount across: hasAboutPage, hasContactPage, hasTestimonialsPage, hasAuthorBox, hasTrustBadges, hasAuthorInfo, and hasExpertiseSignals. E-E-A-T (Experience, Expertise, Authoritativeness, Trustworthiness) is Google's quality rater framework.
Robots Meta & Directives
hasRobotsMeta and robotsContent describe indexing instructions. A canonical does not override noindex. Intentional exclusions are reviewed rather than automatically penalized; where directives conflict, the more restrictive instruction applies.
Favicon & Touch Icons
hasFavicon and hasAppleTouchIcon affect brand recognition in browser tabs, bookmarks, and mobile home screens. Missing favicons look unprofessional in SERPs.
Doctype & Charset
A valid doctype declaration and hasCharset (UTF-8) ensure proper rendering across all browsers and correct character encoding for international content.
Deprecated HTML Tags
deprecatedTags like <font>, <center>, <marquee> signal unmaintained code and may cause rendering inconsistencies across modern browsers.
Meta Refresh Redirect
metaRefresh redirects are discouraged by Google — they prevent proper HTTP status code handling, confuse crawlers, and degrade user experience.
Redirect Chain Length
Each hop in a redirectChainLength wastes crawl budget and adds latency. More than 2 redirects signals poor URL management.
Title–Brand Suffix Duplication
titleBrandSuffixDuplicated detects "| Brand | Brand" patterns where the brand name appears twice in the title, wasting valuable character space.
Cloudflare Email Obfuscation
cfEmailObfuscationDetected with high cfEmailObfuscationCount can interfere with crawlers parsing contact information and may affect E-E-A-T signals.
Zero-Click Optimization
zeroClickOptimized checks for TL;DR blocks (tldrBlockCount), definition lead paragraphs (definitionLeadParagraphs), and hasTableOfContents — content patterns that win featured snippets and AI Overviews.
Entity Salience & Mentions
entitySalienceScore measures how prominently key entities appear. relatedEntityMentions and entityMentions help search engines build topical authority graphs.
Bingeworthy Content Signals
bingeworthySignals detect internal content loops, "related posts" sections, and series navigation that increase session duration and pages per visit.
Enhanced OG Image & Article Dates
ogImageUrl quality, articlePublishedTime, and articleModifiedTime provide temporal signals for content freshness in social shares and news carousels.
Hreflang on Non-Canonical Pages
hreflangOnNonCanonical is a critical error: serving hreflang tags on non-canonical URLs creates conflicting language signals that can cause wrong-language pages in SERPs.
BreadcrumbList Schema Coverage · v5.0
When breadcrumb navigation is present in the UI but no BreadcrumbList JSON-LD is found, Google cannot render breadcrumb rich results. We detect breadcrumb UI (nav[aria-label="breadcrumb"], ol[itemtype*="BreadcrumbList"]) and cross-check for matching structured data.
E-E-A-T Authorship Linkage · v5.0
We check for verifiable authorship markup — rel="author", itemprop="author", and sameAs identity links. Author linkage strengthens Google's Experience/Expertise signals; its absence weakens the trust half of E-E-A-T.
IndexNow Protocol Adoption · v5.0
We look for an IndexNow signal (an indexnow reference or the {key}.txt verification file in robots.txt). Adopting IndexNow (Bing/Yandex/Seznam) pushes instant crawl notifications when content changes, accelerating discovery of new and updated pages.
Multiple Title Tags · v5.1
Whether the multiple title tags signal was detected. Multiple <title> tags detected ([observed value]) — search engines may use an unintended title
Title Tag Count · v5.1
Number of title elements in the HTML. This is the measured count underlying the multiple-title warning.
Multiple Meta Descriptions · v5.1
Whether the multiple meta descriptions signal was detected. Multiple meta descriptions detected ([observed value]) — engines may ignore them or pick the wrong one
Meta Description Count · v5.1
Number of meta description elements. More than one can create ambiguous snippet instructions.
Meta Keywords Length · v5.1
Character length of the meta keywords value. This is descriptive metadata; Google does not use the meta keywords tag for web search ranking.
Hreflang Element Count · v5.1
The recorded hreflang element count value. [observed value] hreflang annotation(s) present for international targeting
Meta Refresh Url · v5.1
Destination declared by a meta refresh instruction, when present. Inspect the destination and prefer an appropriate server redirect where possible.
Nofollow Internal Links · v5.1
Numeric value for nofollow internal links. [observed value] internal link(s) use rel="nofollow" — this wastes internal link equity
Charset Type · v5.1
The recorded charset type value. Character encoding declared as [observed value]
Content Type Header · v5.1
The HTTP Content-Type response value, including its declared character encoding when supplied.
Conflicting Character Encoding · v5.1
Whether the conflicting character encoding signal was detected. Conflicting character-encoding declarations detected — can cause garbled text and indexing issues
Has Frames · v5.1
Whether the frames signal was detected. Legacy frames/framesets detected ([observed value]) — content inside frames is poorly indexed
Frameset Count · v5.1
Number of legacy frameset elements detected; these are separate from modern iframe embeds.
Css Validation Hints · v5.1
The observed list of css validation hints. Inline CSS validation hints: [observed value]
Broken Images · v5.1
Numeric value for broken images. [observed value] image(s) have a missing or empty src — broken images hurt UX and image search
Canonical absolute · v5.2
Canonical must be an absolute HTTP(S) URL. A failure deducts two points; pass, review and not applicable deduct none.
Canonical fragment · v5.2
Canonical must not identify a fragment. A failure deducts two points; pass, review and not applicable deduct none.
Canonical head · v5.2
Canonical annotations belong in the document head. A failure deducts two points; pass, review and not applicable deduct none.
Pagination canonical · v5.2
Distinct paginated content needs its own canonical; review whether this URL is a duplicate filter. A failure deducts two points; pass, review and not applicable deduct none.
Pagination noindex · v5.2
Review noindex on genuine pagination; preserve exclusions for filtered duplicates. A failure deducts two points; pass, review and not applicable deduct none.
Header meta index conflict · v5.2
Conflicting index directives: the more restrictive directive applies. A failure deducts two points; pass, review and not applicable deduct none.
Url template links · v5.2
Search templates belong in metadata, not literal crawlable links. A failure deducts two points; pass, review and not applicable deduct none.

Current source-derived deductions — 56 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Deduct 5 points
Conditional deduction; see the exact trigger.
When: NOT (s.responseTimeMs < 500) AND (s.responseTimeMs < 1000)
Deduct 12 points
Slow server response: [observed value]ms
When: NOT (s.responseTimeMs < 500) AND NOT (s.responseTimeMs < 1000) AND (s.responseTimeMs < 2000)
Deduct 20 points
Very slow server response: [observed value]ms
When: NOT (s.responseTimeMs < 500) AND NOT (s.responseTimeMs < 1000) AND NOT (s.responseTimeMs < 2000)
Deduct 5 points
Large HTML document: [observed value]KB
When: NOT (p.htmlSizeKb < 100) AND NOT (p.htmlSizeKb < 300) AND (p.htmlSizeKb < 500)
Deduct 15 points
Very large HTML: [observed value]KB
When: NOT (p.htmlSizeKb < 100) AND NOT (p.htmlSizeKb < 300) AND NOT (p.htmlSizeKb < 500) AND NOT (isModernFramework && p.htmlSizeKb < 1500)
Deduct 5 points
[observed value] render-blocking script(s)
When: NOT (p.renderBlockingScripts === 0) AND NOT (p.renderBlockingScripts <= 3 && isModernFramework) AND (p.renderBlockingScripts <= 2)
Deduct 12 points
[observed value] render-blocking scripts slow down initial paint
When: NOT (p.renderBlockingScripts === 0) AND NOT (p.renderBlockingScripts <= 3 && isModernFramework) AND NOT (p.renderBlockingScripts <= 2)
Deduct 8 points
No lazy loading on images
When: NOT (p.imageCount > 0 && p.hasLazyImages) AND (p.imageCount > 5 && !p.hasLazyImages)
Deduct 6 points
No WebP or AVIF image formats found
When: NOT (p.hasWebpAvif) AND (p.imageCount > 0)
Deduct 5 points
No responsive image srcset found
When: NOT (p.hasSrcset) AND (p.imageCount > 3)
Deduct 5 points
Only [observed value]/[observed value] images have width/height attributes
When: NOT (p.imageCount > 0 && p.imagesWithWidthHeight >= p.imageCount * 0.8) AND (p.imageCount > 3 && p.imagesWithWidthHeight < p.imageCount * 0.5)
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.hasPreconnect)
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.hasPreload)
Deduct 3 points
No font-display property found
When: NOT (p.hasFontDisplay)
Deduct 5 points
No effective Cache-Control header
When: NOT (cc && (cc.includes('max-age') || cc.includes('s-maxage')))
Deduct 3 points
[observed value] inline <style> blocks
When: (p.inlineStyles > 5)
Deduct 3 points
[observed value] inline scripts
When: (p.inlineScripts > 10 && !isModernFramework)
Deduct 8 points
Large CSS payload (~[observed value]KB across [observed value] files) — slows rendering
When: (p.totalCssKb > 500)
Deduct 3 points
Moderate CSS payload (~[observed value]KB)
When: NOT (p.totalCssKb > 500) AND (p.totalCssKb > 200)
Deduct 10 points
Heavy JavaScript payload (~[observed value]KB across [observed value] files)
When: (p.totalJsKb > 1000 && !isModernFramework)
Deduct 5 points
Moderate JavaScript payload (~[observed value]KB)
When: NOT (p.totalJsKb > 1000 && !isModernFramework) AND (p.totalJsKb > 500 && !isModernFramework)
Deduct 5 points
Large JS bundle (~[observed value]KB) even for framework app
When: NOT (p.totalJsKb > 1000 && !isModernFramework) AND NOT (p.totalJsKb > 500 && !isModernFramework) AND (p.totalJsKb > 2000 && isModernFramework)
Deduct 4 points
Redirect chain ([observed value] hops) adds latency to page load
When: (p.redirectChainLength > 2)
Deduct 4 points
[observed value] links increase DOM size and parsing time
When: (p.linkCount > 200)
Deduct 4 points
HTTP/1.1 detected — no multiplexing
When: NOT (p.httpVersion === 'HTTP/3') AND NOT (p.httpVersion === 'HTTP/2') AND (!isModernFramework)
Deduct 5 points
No CDN detected
When: NOT (p.cdnDetected)
Deduct 6 points
No response compression detected
When: NOT (p.compressionType === 'br') AND NOT (p.compressionType === 'gzip') AND (p.compressionType === 'none')
Deduct 6 points
[observed value] third-party scripts from [observed value] domains — significant performance impact
When: (p.thirdPartyScriptCount > 10)
Deduct 3 points
[observed value] third-party scripts detected
When: NOT (p.thirdPartyScriptCount > 10) AND (p.thirdPartyScriptCount > 5)
Deduct 4 points
No resource hints found
When: NOT (p.totalResourceHints >= 5) AND (p.totalResourceHints === 0)
Deduct 4 points
[observed value] font files loaded — excessive font weight
When: (p.fontFileCount > 4)
Deduct 2 points
Conditional deduction; see the exact trigger.
When: NOT (p.preloadFontCount > 0) AND (p.fontFileCount > 0)
Deduct 5 points
[observed value] render-blocking CSS files — delays first paint
When: (p.renderBlockingCssCount > 3)
Deduct 2 points
[observed value] render-blocking CSS file(s)
When: NOT (p.renderBlockingCssCount > 3) AND (p.renderBlockingCssCount > 1 && !isModernFramework)
Deduct 4 points
Estimated CSS bundle ~[observed value]KB — above optimal
When: (p.totalCssBundleKb > 300)
Deduct 5 points
Estimated JS bundle ~[observed value]KB — heavy payload
When: (p.totalJsBundleKb > 1500 && !isModernFramework)
Deduct 3 points
Large JS bundle (~[observed value]KB) even for framework app
When: NOT (p.totalJsBundleKb > 1500 && !isModernFramework) AND (p.totalJsBundleKb > 3000 && isModernFramework)
Deduct 2 points
Potentially unused CSS detected (large stylesheet count relative to page complexity)
When: (p.unusedCssHint)
Deduct Math.min(p.longTaskScriptHints.length * 2, 6) points
Potential long-task scripts: [observed value]
When: (p.longTaskScriptHints.length > 0 && !isModernFramework)
Deduct 5 points
Estimated LCP: ~[observed value]ms — needs improvement (goal: <2.5s)
When: (p.lcpEstimateMs > 0) AND NOT (p.lcpEstimateMs <= 2500) AND (p.lcpEstimateMs <= 4000)
Deduct 10 points
Poor estimated LCP: ~[observed value]ms — exceeds 4s threshold
When: (p.lcpEstimateMs > 0) AND NOT (p.lcpEstimateMs <= 2500) AND NOT (p.lcpEstimateMs <= 4000)
Deduct 8 points
High estimated CLS: [observed value] — exceeds Google's 0.25 threshold
When: (p.clsEstimate > 0.25)
Deduct 4 points
Moderate estimated CLS: [observed value] — above 0.1 "good" threshold
When: NOT (p.clsEstimate > 0.25) AND (p.clsEstimate > 0.1)
Deduct 4 points
Estimated INP: ~[observed value]ms — needs improvement (goal: <200ms)
When: (p.inpEstimateMs > 0) AND NOT (p.inpEstimateMs <= 200) AND (p.inpEstimateMs <= 500 && !isModernFramework)
Deduct 2 points
Estimated INP: ~[observed value]ms — slightly above threshold for framework app
When: (p.inpEstimateMs > 0) AND NOT (p.inpEstimateMs <= 200) AND NOT (p.inpEstimateMs <= 500 && !isModernFramework) AND NOT (p.inpEstimateMs <= 300) AND (p.inpEstimateMs <= 500)
Deduct 8 points
Poor estimated INP: ~[observed value]ms
When: (p.inpEstimateMs > 0) AND NOT (p.inpEstimateMs <= 200) AND NOT (p.inpEstimateMs <= 500 && !isModernFramework) AND NOT (p.inpEstimateMs <= 300) AND NOT (p.inpEstimateMs <= 500)
Deduct 3 points
Web fonts loaded without preload — causes FOIT/FOUT flash
When: (p.fontPreloadMissed)
Deduct 5 points
Basic analytics only — missing event tracking or tag management
When: NOT (p.analyticsSetupQuality === 'comprehensive') AND NOT (p.analyticsSetupQuality === 'good') AND (p.analyticsSetupQuality === 'basic')
Deduct 6 points
No analytics tracking detected — flying blind on user behavior
When: NOT (p.analyticsSetupQuality === 'comprehensive') AND NOT (p.analyticsSetupQuality === 'good') AND NOT (p.analyticsSetupQuality === 'basic') AND NOT (p.analyticsSetupQuality === 'alternative')
Deduct 5 points
Excessive DOM size ([observed value] elements) — increases memory use and slows rendering
When: (p.domElementCount > 1500)
Deduct 3 points
Inline CSS does not appear minified
When: (!p.cssMinified)
Deduct 3 points
Inline JavaScript does not appear minified
When: (!p.jsMinified)
Deduct 5 points
document.write() used [observed value] time(s) — blocks the parser and delays rendering
When: (p.documentWriteCount > 0)
Deduct 4 points
No efficient cache policy (Cache-Control max-age) on the main document
When: NOT (p.hasEfficientCachePolicy)
Deduct 3 points
[observed value] animated GIF(s) detected — GIFs are far larger than modern video formats
When: (p.animatedGifCount > 0)
Deduct 4 points
[observed value] image(s) lack explicit width/height — a common cause of layout shift (CLS)
When: (p.imagesWithoutDimensions > 0)
LCP Estimate (Largest Contentful Paint)
lcpEstimateMs is our proxy for the largest above-fold element render time. We estimate from hero image size, font preloading, render-blocking resources, and server timing. Google threshold: ≤2.5s good, ≤4s needs improvement.
CLS Estimate (Cumulative Layout Shift)
clsEstimate proxies visual stability from images without width/height attributes (imagesWithWidthHeight), font loading strategy, dynamic content injection, and ad placeholders. Google threshold: ≤0.1 good.
INP Estimate (Interaction to Next Paint)
inpEstimateMs proxies input responsiveness from JavaScript bundle size (totalJsBundleKb), long task script hints (longTaskScriptHints), third-party script count, and main-thread blocking indicators. Google threshold: ≤200ms good.
HTML Document Size
htmlSizeKb over 100KB indicates bloated markup. Excessive inline styles (inlineStyles) and inline scripts (inlineScripts) inflate document size and delay first render.
CSS Bundle Size & Optimization
totalCssBundleKb measures all CSS payload. renderBlockingCssCount identifies files blocking first paint. unusedCssHint detects dead CSS. criticalCssDetected rewards above-fold CSS inlining.
JavaScript Bundle Size & Loading
totalJsBundleKb measures total JS payload. We check asyncScripts, deferScripts vs. renderBlockingScripts ratio. longTaskScriptHints identify scripts likely to cause long tasks (>50ms).
Image Optimization
hasLazyImages, lazyImageCount, eagerAboveFold (should be 1–3), hasSrcset for responsive images, and hasWebpAvif for modern formats. imageFormats breakdown reveals JPEG/PNG legacy vs. WebP/AVIF modern usage.
Font Loading Strategy
hasFontDisplay (font-display: swap/optional) prevents FOIT. fontPreloadMissed detects critical fonts not preloaded. variableFontsUsed rewards modern font technology. fontFileCount over 4 indicates excessive font requests.
Resource Hints (Preconnect/Prefetch/Preload)
hasPreconnect, hasPrefetch, hasPreload, and totalResourceHints measure proactive resource loading. preconnectDomains and preloadTypes (font, script, style) show implementation depth. preloadFontCount validates font preloading.
Async/Defer Script Optimization
renderBlockingScripts (neither async nor defer) delay parsing. The ratio of asyncScripts + deferScripts to total scripts indicates optimization maturity.
Third-Party Script Impact
thirdPartyScriptCount and thirdPartyDomains measure external dependency load. Each third-party domain adds DNS lookup, connection, and TLS handshake overhead.
CDN & Compression
cdnDetected and cdnProvider indicate edge caching. compressionType (br > gzip > none) directly affects transfer size. Brotli compression reduces payloads 15–20% more than gzip.
Server Response Time (TTFB)
ttfbMs measures time to first byte. Under 200ms is excellent; over 600ms indicates server-side bottlenecks. httpVersion (HTTP/2 or HTTP/3 enables multiplexing).
Server Timing Headers
serverTimingHeaders expose backend performance metrics (db, cache, render times) for debugging. Their presence signals performance-aware engineering.
Framework Detection & SPA Analysis
isModernFramework, spaDetected, and jsFrameworkDetected identify React, Next.js, Vue, Angular, etc. SPAs require special SSR/SSG consideration for SEO and initial load performance.
Analytics Setup Quality
We validate hasGA4Snippet, hasGTMContainer, ga4MeasurementId, hasEventTracking, and compute analyticsSetupQuality. Dual GA4+GTM setup without event tracking wastes data collection potential.
Image Width/Height Attributes
imagesWithWidthHeight prevents CLS. Images without explicit dimensions cause layout shifts when they load, directly impacting Core Web Vitals CLS score.
Speculation Rules API · v5.0
We detect <script type="speculationrules">, the modern API that prerenders/prefetches likely next navigations for near-instant page transitions. Its absence is a missed opportunity for perceived-instant navigation on multi-page journeys.
LCP fetchpriority Hint · v5.0
We check whether any image declares fetchpriority="high". Marking the LCP hero image with fetchpriority="high" lets the browser prioritize it during initial load, directly improving Largest Contentful Paint.
Image decoding="async" Coverage · v5.0
On pages with 4+ images we measure the share carrying decoding="async". Below 50% coverage, image decode work competes with the main thread. Adding decoding="async" to non-critical images keeps decoding off the main thread.
modulepreload for ES Modules · v5.0
When ES modules (type="module") are used without <link rel="modulepreload">, the browser discovers dependencies late, creating waterfall latency on the module graph. Preloading critical modules removes that latency.
Images Without Dimensions · v5.1
Numeric value for images without dimensions. [observed value] image(s) lack explicit width/height — a common cause of layout shift (CLS)
Animated Gif Count · v5.1
Numeric value for animated gif count. [observed value] animated GIF(s) detected — GIFs are far larger than modern video formats
Dom Element Count · v5.1
The recorded dom element count value. Excessive DOM size ([observed value] elements) — increases memory use and slows rendering
Has Efficient Cache Policy · v5.1
Whether the efficient cache policy signal was detected. Efficient cache policy on the document ([observed value])
Cache Control Value · v5.1
The received Cache-Control header used to explain the cache-policy assessment. The right policy depends on whether content is public or sensitive.
Css Minified · v5.1
Whether the css minified signal was detected. Inline CSS does not appear minified
Js Minified · v5.1
Whether the js minified signal was detected. Inline JavaScript does not appear minified
Document Write Count · v5.1
The recorded document write count value. document.write() used [observed value] time(s) — blocks the parser and delays rendering
Speed Index Estimate Ms · v5.1
Deterministic Speed Index proxy in milliseconds inferred from source and resource composition. Not a browser paint measurement or field-data result.
Tti Estimate Ms · v5.1
Deterministic time-to-interactive proxy in milliseconds. It is not measured interactive readiness on a real device.
Tbt Estimate Ms · v5.1
Deterministic total-blocking-time proxy in milliseconds. It is not measured browser long-task duration.
High priority lazy image · v5.2
High-priority images should not be lazy-loaded; actual LCP requires a browser measurement. A failure deducts two points; pass, review and not applicable deduct none.

Current source-derived deductions — 58 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Deduct 25 points
Site does not use HTTPS
When: NOT (p.isHttps)
Deduct 5 points
No HTTP → HTTPS redirect configured
When: NOT (p.isHttps) AND (!s.httpToHttpsRedirect)
Deduct 10 points
Mixed content detected (HTTP resources on HTTPS page)
When: (p.hasMixedContent)
Deduct 10 points
Missing Strict-Transport-Security header
When: NOT (hdrs['strict-transport-security'])
Deduct 10 points
Missing Content-Security-Policy header
When: NOT (hdrs['content-security-policy'])
Deduct 5 points
Missing X-Content-Type-Options header
When: NOT (hdrs['x-content-type-options'])
Deduct 5 points
Missing X-Frame-Options header
When: NOT (hdrs['x-frame-options'])
Deduct 5 points
Missing Referrer-Policy header
When: NOT (hdrs['referrer-policy'])
Deduct 5 points
Missing Permissions-Policy header
When: NOT (hdrs['permissions-policy'])
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (hdrs['cross-origin-opener-policy'])
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (hdrs['cross-origin-resource-policy'])
Deduct 2 points
Conditional deduction; see the exact trigger.
When: NOT (hdrs['cross-origin-embedder-policy'])
Deduct 3 points
Server header exposes technology: [observed value]
When: NOT (!server || server === 'not exposed') AND NOT (isCdnServer)
Deduct 3 points
X-Powered-By header exposes: [observed value]
When: NOT (!poweredBy || poweredBy === 'not exposed') AND NOT (isFrameworkPowered)
Deduct 5 points
[observed value] external links missing rel="noopener noreferrer" — security risk
When: (p.externalLinksWithoutRel > 5)
Deduct 2 points
[observed value] external link(s) without noopener/noreferrer
When: NOT (p.externalLinksWithoutRel > 5) AND (p.externalLinksWithoutRel > 0)
Deduct 3 points
HSTS header present but missing preload directive
When: NOT (p.hstsPreload && p.hstsMaxAge >= 31536000) AND (hdrs['strict-transport-security'] && !p.hstsPreload)
Deduct 3 points
HSTS max-age is only [observed value] days — should be at least 1 year
When: (p.hstsMaxAge > 0 && p.hstsMaxAge < 31536000)
Deduct Math.min(8, uniqueIssueTypes.length * 3) points
Cookie security issues: [observed value]
When: (p.cookieSecurityIssues.length > 0)
Deduct 8 points
[observed value] form(s) submit to insecure HTTP endpoints
When: (p.formActionsInsecure > 0)
Deduct 3 points
No privacy policy detected
When: NOT (p.hasPrivacyPolicy)
Deduct 3 points
No cookie consent banner detected
When: NOT (p.hasCookieConsent)
Deduct 3 points
No Subresource Integrity (SRI) on external scripts
When: NOT (p.hasSubresourceIntegrity) AND (p.thirdPartyScriptCount > 0)
Deduct 6 points
Weak CSP implementation (strictness score: [observed value]/100)
When: (p.cspStrictnessScore > 0) AND NOT (p.cspStrictnessScore >= 70) AND NOT (p.cspStrictnessScore >= 40)
Deduct Math.min(5, p.cspUnsafeInlineCount * 2) points
CSP contains [observed value] unsafe-inline directive(s) — weakens XSS protection
When: (p.cspStrictnessScore > 0) AND (p.cspUnsafeInlineCount > 0 && !p.isModernFramework)
Deduct Math.min(8, p.outdatedLibraryHints.length * 3) points
Outdated libraries detected: [observed value]
When: (p.outdatedLibraryHints.length > 0)
Deduct 8 points
Server-side software version disclosed: [observed value] — enables version-based vulnerability (CVE) lookups
When: (p.frameworkVersionExposed)
Deduct 3 points
Server technology disclosed via response headers/markup: [observed value]
When: (p.serverTechExposed.length > 0) AND (versionless.length > 0 && !p.frameworkVersionExposed)
Deduct 2 points
security.txt found but has issues: [observed value]
When: NOT (p.securityTxtExists && p.securityTxtValid && p.securityTxtIssues.length === 0) AND (p.securityTxtExists && p.securityTxtIssues.length > 0)
Deduct 3 points
security.txt file is missing — no standardized channel for reporting vulnerabilities
When: NOT (p.securityTxtExists && p.securityTxtValid && p.securityTxtIssues.length === 0) AND NOT (p.securityTxtExists && p.securityTxtIssues.length > 0)
Deduct 8 points
Requesting a non-existent path returned HTTP [observed value] (Internal Server Error) instead of a clean 404 — unhandled exception
When: (p.serverErrorDetected)
Deduct Math.min(15, p.errorLeakagePatterns.length * 6) points
Error output leaks sensitive internal details: [observed value]
When: (p.errorLeakagePatterns.length > 0)
Deduct Math.min(12, p.debugInfoLeakage.length * 5) points
Debug/verbose diagnostics exposed: [observed value]
When: (p.debugInfoLeakage.length > 0)
Deduct 15 points
A password/login field submits over an insecure (HTTP) form action — credentials transmitted in clear text
When: (p.passwordFieldInsecureForm)
Deduct 10 points
HTTP Basic Authentication offered over an unencrypted channel (WWW-Authenticate over HTTP) — credentials are base64-encoded, not encrypted
When: (p.basicAuthOverHttp)
Deduct 4 points
[observed value] insecure ws:// WebSocket endpoint(s) referenced — data exchanged unencrypted
When: (p.insecureWebSocketCount > 0)
Deduct 2 points
Legacy X-XSS-Protection header present — deprecated and can introduce vulnerabilities in older browsers
When: (p.xssProtectionLegacy)
Deduct 3 points
Content-Security-Policy is delivered only via a <meta> tag — cannot use report-uri/frame-ancestors and is bypassable
When: (p.cspViaMetaOnly)
Deduct Math.min(6, p.cspMissingDirectives.length * 2) points
CSP is missing key hardening directive(s): [observed value]
When: (p.cspMissingDirectives.length > 0 && (hdrs['content-security-policy'] || p.cspViaMetaOnly))
Deduct 4 points
A sensitive page (login/account/checkout) lacks Cache-Control: no-store — confidential data may be cached by browsers/proxies
When: (p.missingCacheControlOnSensitive)
Deduct 6 points
A session identifier appears to be passed in the URL — session IDs in URLs leak via referrer headers, logs, and browser history
When: (p.sessionIdInUrl)
Deduct 2 points
A password field does not disable autocomplete on a shared/sensitive context — may persist credentials on shared devices
When: (p.autocompleteOnPasswordField)
Deduct Math.min(6, p.stateChangingFormsWithoutCsrf * 3) points
[observed value] state-changing POST form(s) show no visible anti-CSRF token (heuristic — SameSite cookies may still protect them)
When: (p.stateChangingFormsWithoutCsrf > 0)
Deduct Math.min(6, p.dangerousJsSinks.length * 2) points
Dangerous client-side sink(s) detected in inline scripts: [observed value] — potential DOM-based XSS vectors
When: (p.dangerousJsSinks.length > 0)
Deduct 2 points
[observed value] inline event handlers (onclick=, onload=, …) — inline handlers require CSP unsafe-inline and widen the XSS surface
When: (p.inlineEventHandlerCount > 10)
Deduct 2 points
[observed value] javascript: URL(s) found — a legacy XSS/injection vector
When: (p.javascriptUrlCount > 0)
Deduct 3 points
Possible open-redirect parameter(s) in links: [observed value] — can be abused for phishing (CWE-601)
When: (p.openRedirectParams.length > 0)
Deduct Math.min(8, p.untrustedCrossDomainScripts.length * 4) points
[observed value] script(s) loaded from higher-risk / abandoned third-party origins (e.g. [observed value]) — supply-chain risk
When: (p.untrustedCrossDomainScripts.length > 0)
Deduct Math.min(40, p.exposedSecrets.length * 20) points
Possible secret/API key pattern(s) exposed in page source: [observed value] — CRITICAL if these are live credentials (CWE-798)
When: (p.exposedSecrets.length > 0)
Deduct Math.min(30, p.exposedSensitivePaths.length * 15) points
Sensitive path(s) publicly accessible: [observed value] — may expose source control, environment secrets or internals
When: (p.exposedSensitivePaths.length > 0)
Deduct 8 points
Directory listing appears enabled — file/folder structure is browsable by anyone
When: (p.directoryListingDetected)
Deduct Math.min(8, highRisk.length * 4) points
Potentially dangerous HTTP method(s) advertised: [observed value] — TRACE/TRACK enable XST and PUT/DELETE may allow unauthorized changes
When: (p.dangerousHttpMethods.length > 0) AND (highRisk.length > 0)
Deduct 2 points
robots.txt discloses sensitive-looking path(s): [observed value] — Disallow entries can act as a map for attackers
When: (p.robotsSensitivePaths.length > 0)
Deduct Math.min(4, p.htmlCommentLeaks.length * 2) points
HTML comments leak potentially sensitive hints: [observed value]
When: (p.htmlCommentLeaks.length > 0)
Deduct 4 points
A private/internal IP address (RFC 1918) is disclosed in the page source — reveals internal network topology
When: (p.privateIpDisclosure && p.privateIpDisclosure.length > 0)
Deduct 10 points
CORS reflects the request Origin while allowing credentials — effectively allows any site to make authenticated cross-origin requests
When: (p.corsReflectedWithCredentials)
Deduct 5 points
Access-Control-Allow-Origin: * — the API is readable by any website
When: NOT (p.corsReflectedWithCredentials) AND (p.corsWildcard)
Deduct Math.min(4, p.iframeSandboxMissing * 2) points
[observed value] untrusted <iframe>(s) without a sandbox attribute — embedded content runs with full privileges
When: (p.iframeSandboxMissing > 0)
HTTPS Encryption
isHttps verifies TLS encryption. Without HTTPS, browsers display "Not Secure" warnings, forms transmit data in plaintext, and Google applies a ranking penalty.
Mixed Content
hasMixedContent detects HTTP resources loaded on HTTPS pages. Browsers may block these resources, breaking functionality and displaying security warnings.
HSTS (Strict-Transport-Security)
HSTS forces HTTPS-only connections. We check hstsMaxAge (≥ 31536000 recommended) and hstsPreload (inclusion in browser preload lists for zero-trust-on-first-use protection).
Content-Security-Policy (CSP)
CSP is the primary defense against XSS attacks. A missing CSP leaves your site vulnerable to injected scripts that can steal cookies, credentials, and user data.
CSP Strictness Score
cspStrictnessScore (0–100) evaluates directive count, cspUsesNonce vs. cspUnsafeInlineCount, frame-ancestors restrictions, and default-src fallbacks. Nonce-based CSP is significantly stronger than unsafe-inline.
Permissions-Policy Depth
permissionsPolicyFeatures and permissionsPolicyDepth measure how many browser APIs (camera, microphone, geolocation, payment) are explicitly restricted. Without this header, any embedded iframe can access sensitive device features.
X-Content-Type-Options
The nosniff directive in headers prevents MIME-type sniffing attacks where browsers misinterpret file types, potentially executing malicious content.
X-Frame-Options
Prevents clickjacking by controlling iframe embedding. DENY or SAMEORIGIN values stop malicious sites from framing your pages.
Referrer-Policy
Controls how much URL information leaks via the Referer header. strict-origin-when-cross-origin balances analytics needs with privacy.
Cross-Origin Policies (COOP/CORP/COEP)
hasCorsHeaders and cross-origin isolation headers protect against Spectre side-channel attacks by isolating your page's browsing context from cross-origin resources.
Outdated Library Detection
jqueryVersion and outdatedLibraryHints detect libraries with known CVEs: jQuery <3.5 (XSS), Angular <1.8 (sandbox escapes), Bootstrap <5 (XSS via data attributes).
Cookie Security
cookieSecurityIssues identifies cookies without Secure (sent over HTTP), HttpOnly (accessible to JavaScript), and SameSite (vulnerable to CSRF) attributes.
Insecure Form Actions
formActionsInsecure counts forms submitting to HTTP endpoints, transmitting user data in plaintext — a PCI-DSS and GDPR violation.
Subresource Integrity (SRI)
hasSubresourceIntegrity and sriCount verify that external scripts include integrity hashes, preventing supply-chain attacks where CDN-hosted scripts are modified.
External Link Security
externalLinksWithoutRel counts links with target="_blank" but missing rel="noopener noreferrer", enabling reverse tabnabbing attacks.
Privacy Policy & Cookie Consent
hasPrivacyPolicy and hasCookieConsent are GDPR/CCPA legal requirements. Non-compliance can result in fines up to 4% of annual global revenue.
Rate Limiting
hasRateLimitHeaders (X-RateLimit-*, Retry-After) indicate protection against brute-force attacks, credential stuffing, and API abuse.
Server/Technology Exposure
headers like Server, X-Powered-By, and X-AspNet-Version expose your technology stack, giving attackers a targeted roadmap of known vulnerabilities.
Framework / Server Version Disclosure
frameworkVersionExposed detects a specific software version leaked in the Server or X-Powered-By header or the generator meta tag (e.g. "nginx/1.18.0", "PHP/7.4.3", "WordPress 5.9"). A precise version lets an attacker look up the exact list of published CVEs affecting your stack — the single highest-value fingerprint for targeted exploitation.
Technology Fingerprint Exposure
serverTechExposed aggregates every technology-revealing signal in your headers and markup (Server, X-Powered-By, X-AspNet-Version, X-Generator, framework-specific cookies and comment signatures). Even without a version number, each fingerprint narrows the attacker's search space and enables stack-specific attacks.
security.txt Vulnerability Disclosure (RFC 9116)
We fetch /.well-known/security.txt and validate it per RFC 9116 (securityTxtPresent, securityTxtLocation, securityTxtIssues). A valid, future-dated file with a Contact and Expires field gives ethical researchers a standardized channel to report vulnerabilities responsibly instead of disclosing them publicly.
Internal Server Error Probe (5xx Handling)
We request a deliberately non-existent path and inspect the response (errorProbeStatusCode). A clean 404 is correct; a 500-class Internal Server Error on a missing route signals an unhandled exception that can leak internal state and indicates fragile error handling.
Error & Stack Trace Leakage
errorLeakagePatterns scans responses for exposed stack traces, absolute file paths, SQL fragments, and framework debug output. Leaked traces hand attackers a map of your file system, framework internals, and query structure — one of the most damaging low-effort information disclosures (CWE-209).
Debug & Verbose Diagnostics Exposure
debugInfoLeakage detects debug mode left enabled in production: profiler toolbars, verbose diagnostic headers, and development-only banners. Debug output erodes the defense-in-depth "security by obscurity" layer and frequently reveals environment variables and internal routes.
Email Address Harvesting Exposure
exposedEmails / exposedEmailCount surface raw email addresses printed in the page source. Intentional contact addresses are informational only, but unprotected mailto and inline addresses are trivially scraped by spam and phishing bots — obfuscation or a contact form is recommended.
Exposed Secrets & API Keys · v4.9.1
exposedSecrets scans inline scripts and markup for high-signal credential patterns (AWS access keys, Stripe live keys, Google API keys, GitHub/Slack tokens, JWTs, SendGrid keys, private-key blocks and inline Firebase config). Hard-coded live credentials in client-side code are a critical CWE-798 exposure — anyone can read and abuse them. Maps to OWASP A07.
Publicly Accessible Sensitive Paths · v4.9.1
exposedSensitivePaths performs non-destructive GET probes of common leak points (/.git/config, /.env, /.svn/entries, /.DS_Store, /server-status) with content-based confirmation. Exposed VCS folders or environment files can hand attackers your entire source and secrets (CWE-538/CWE-548, OWASP A05).
Password Field on Insecure Form · v4.9.1
passwordFieldInsecureForm flags any form containing a password input whose action resolves to http://. Credentials are then transmitted in clear text and trivially intercepted (CWE-319, ASVS 9.1, OWASP A02).
CORS Reflected Origin with Credentials · v4.9.1
corsReflectedWithCredentials detects Access-Control-Allow-Credentials: true combined with a reflected or wildcard Origin — effectively letting any website make authenticated cross-origin requests to your API (CWE-942/CWE-346, OWASP A05).
CORS Wildcard Origin · v4.9.1
corsWildcard flags Access-Control-Allow-Origin: *, which makes API responses readable by any site. A strict server-side origin allowlist is recommended.
HTTP Basic Auth over HTTP · v4.9.1
basicAuthOverHttp detects a WWW-Authenticate: Basic challenge served over an unencrypted channel. Basic-Auth credentials are only base64-encoded, so they are effectively sent in the clear (CWE-319).
Directory Listing Enabled · v4.9.1
directoryListingDetected identifies auto-generated index pages that expose your file/folder structure to anyone. Disable autoindex / Options -Indexes (CWE-548, OWASP A05).
Dangerous HTTP Methods · v4.9.1
dangerousHttpMethods inspects the OPTIONS Allow header for TRACE/TRACK (enable Cross-Site Tracing) and PUT/DELETE/CONNECT/PATCH (may allow unauthorized modification). Only the verbs your app needs should be enabled (CWE-650).
DOM-XSS Sinks in Inline Scripts · v4.9.1
dangerousJsSinks detects risky client-side sinks (eval, document.write, innerHTML assignment, Function(), setTimeout with a string, insertAdjacentHTML). Combined with untrusted input these are classic DOM-based XSS vectors (CWE-79, OWASP A03).
Untrusted / Abandoned Third-Party Scripts · v4.9.1
untrustedCrossDomainScripts flags scripts loaded from higher-risk or historically compromised CDNs (e.g. polyfill-style hosts). Supply-chain compromise of a single script can run arbitrary code on every page (CWE-1104/CWE-829, OWASP A08).
Software Composition Inventory (SBOM) · v4.9.1
scriptInventoryCount and detectedLibraries catalogue every script resource and fingerprinted client-side library, giving you a lightweight software bill of materials to cross-reference against known CVEs.
Session ID in URL · v4.9.1
sessionIdInUrl detects session-token-like parameters in URLs. Session IDs in URLs leak through Referer headers, server logs, and browser history and enable session fixation (CWE-598/CWE-384, ASVS 3.x).
Anti-CSRF Token Heuristic · v4.9.1
stateChangingFormsWithoutCsrf counts state-changing POST forms that show no visible anti-CSRF token (search forms excluded). This is a low-confidence heuristic — SameSite cookies may still protect them — but missing tokens warrant review (CWE-352, OWASP A01).
Open Redirect Parameters · v4.9.1
openRedirectParams inspects links for redirect/return/next/url parameters carrying absolute URLs, a common phishing pivot when unvalidated (CWE-601).
Insecure WebSocket (ws://) · v4.9.1
insecureWebSocketCount finds unencrypted ws:// endpoints in markup/scripts. WebSocket traffic should always use the encrypted wss:// scheme (CWE-319).
CSP Delivered via <meta> Only · v4.9.1
cspViaMetaOnly flags a policy delivered only through a meta tag, which cannot enforce frame-ancestors or report-uri and is easier to bypass. Serve the CSP as an HTTP header.
CSP Missing Hardening Directives · v4.9.1
cspMissingDirectives checks for absent object-src, base-uri and frame-ancestors directives — common gaps that leave known CSP bypasses open.
Sensitive-Page Cache-Control · v4.9.1
missingCacheControlOnSensitive verifies login/account/checkout pages send Cache-Control: no-store so confidential data is not cached by browsers or shared proxies (CWE-525, ASVS 8.2).
Untrusted iframe Without sandbox · v4.9.1
iframeSandboxMissing counts iframes embedding third-party origins without a sandbox attribute, meaning embedded content runs with full privileges.
HTML Comment & Private-IP Leakage · v4.9.1
htmlCommentLeaks scans developer comments for TODOs, credentials, internal URLs and debug notes; privateIpDisclosure flags RFC 1918 internal IPs in the source — both reveal internal details useful to attackers (CWE-200).
robots.txt Sensitive-Path Disclosure · v4.9.1
robotsSensitivePaths surfaces admin/private Disallow entries that act as a roadmap for attackers. Protect sensitive paths with authentication rather than obscurity.
Legacy X-XSS-Protection & Inline Handlers · v4.9.1
xssProtectionLegacy flags the deprecated X-XSS-Protection header (can introduce bugs in old browsers); inlineEventHandlerCount and javascriptUrlCount measure inline on*= handlers and javascript: URLs that force CSP unsafe-inline and widen the XSS surface.
Authentication Surface Detection · v4.9.1
hasLoginForm and hasPasswordResetForm identify authentication entry points so the report can recommend rate limiting, MFA, and single-use time-limited reset tokens (OWASP A07). Positive signals: cspHasReporting, clearSiteDataSupported and crossOriginIsolated (COOP+COEP) are recognised as hardening wins.
A01:2025 · Broken Access Control & SSRF · v5.0
A light, non-destructive probe checks whether a privileged/admin path (e.g. /admin, /dashboard, /api/admin) is reachable with real admin markup and no auth challenge. We also flag IDOR-prone direct object references in links and SSRF-style references to cloud instance-metadata endpoints (169.254.169.254). Recommendation references centralized, deny-by-default authorization (OPA / Casbin) and ASVS L2 access-control verification.
A02:2025 · Security Misconfiguration & Source-Map Exposure · v5.0
Probes for publicly readable debug/diagnostic endpoints (phpinfo, Spring Boot /actuator, Symfony /_profiler) and publicly served JavaScript source maps that leak original source. Recommendation references hardened, environment-specific configuration baselines and disabling source maps in production.
A03:2025 · Software Supply Chain Failures · v5.0
Probes for exposed dependency manifests / lockfiles (package.json, composer.json, yarn.lock, Gemfile.lock), measures the Subresource-Integrity coverage ratio across cross-origin scripts (distinct from mere SRI presence), and detects a published SBOM (CycloneDX / SPDX). Recommendation references SLSA provenance and signed, pinned dependencies.
A04:2025 · Cryptographic Failures · v5.0
Detects weak-hash usage (MD5 / SHA-1) in client scripts and sensitive PII form fields submitted in cleartext over HTTP. Recommendation references modern algorithms (SHA-256+/Argon2/bcrypt) and TLS-only transmission of sensitive data.
A05:2025 · Injection · v5.0
Flags raw-HTML binding sinks (React dangerouslySetInnerHTML, Vue v-html), production GraphQL introspection (light probe), and Markdown rendering without a sanitizer. Recommendation references context-aware output encoding and DOMPurify-style sanitization.
A06:2025 · Insecure Design · v5.0
Detects high-value forms (login, signup, password reset, contact) with no anti-automation challenge (CAPTCHA/Turnstile/hCaptcha) and file-upload inputs without type/size constraints. Recommendation references threat modeling and abuse-case-driven design.
A07:2025 · Authentication Failures · v5.0
Checks for the absence of a phishing-resistant factor (WebAuthn / passkey / TOTP MFA), missing autocomplete tokens on identifier fields, and session cookies lacking the __Host- / __Secure- prefix. Recommendation references passkey adoption and hardened session cookies.
A08:2025 · Software & Data Integrity Failures · v5.0
Flags dynamically-injected remote scripts loaded without integrity verification and opaque serialized state passed in URL parameters (deserialization/tampering risk). Recommendation references signed updates and integrity-verified pipelines.
A09:2025 · Security Logging & Alerting Failures · v5.0
Detects the presence/absence of client-side error & security monitoring (Sentry, Datadog RUM, Bugsnag, Rollbar). Absence is surfaced as a recommendation to add tamper-evident, centrally-aggregated monitoring and alerting.
A10:2025 · Mishandling of Exceptional Conditions · v5.0
A random non-existent path is probed; a soft-404 / fail-open response (HTTP 200 for an unknown route) signals exceptional conditions handled insecurely. Recommendation references fail-closed error handling and correct status semantics.
Canonical credentials · v5.2
Canonical must not expose URL credentials. A failure deducts two points; pass, review and not applicable deduct none.

Current source-derived deductions — 0 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Structured Data for AI
hasSchemaOrg and schemaTypes provide machine-readable context. Without structured data, AI must infer meaning from unstructured HTML — a lossy process that reduces citation accuracy.
AI Crawler Governance (robots.txt)
We check 12+ AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, Amazonbot, Bytespider, etc.) across both meta tags (aiCrawlerBlocked) and robots.txt (robotsTxtAiCrawlerRules). additionalAiCrawlersAllowed and additionalAiCrawlersBlocked provide granular per-crawler governance.
Robots.txt Deep Analysis
robotsTxtExists is foundational. We parse robotsTxtSitemapRefs (sitemap declarations), robotsTxtBlocksAssets (CSS/JS blocking hurts rendering), and robotsTxtBlocksAll (Disallow: / blocks everything).
llms.txt File Existence
Absence receives no penalty. Google AI features do not require a special AI text file. If supplied, its content can be reviewed as optional documentation.
llms-full.txt Companion File
llmsFullTxtExists checks for /llms-full.txt, the extended version containing comprehensive content for deep AI ingestion. Together, llmsTxtByteSize and llmsFullTxtByteSize indicate content depth.
llms.txt Structure & Sections
llmsTxtSectionCount measures document organization. llmsTxtHasStructuredSections and llmsTxtHasMarkdownFormatting indicate proper Markdown structure that AI parsers can reliably extract.
llms.txt Metadata Quality
llmsTxtHasTitle, llmsTxtHasDescription, llmsTxtHasVersionOrDate, and llmsTxtHasCanonicalDomain provide essential metadata that helps AI models understand your organization and content currency.
llms.txt URL Inventory
llmsTxtHasUrlList and llmsTxtUrlCount indicate how many pages you've indexed for AI consumption. llmsTxtBrokenUrlPatterns detects URLs in llms.txt that appear malformed or incomplete.
llms.txt Service & API Documentation
llmsTxtHasServiceList and llmsTxtHasApiDocs signal that you've documented your offerings in a format AI agents can parse for tool-use and function-calling scenarios.
llms.txt Legal & Citation Framework
llmsTxtHasLicenseOrTerms, llmsTxtHasPreferredCitation, and llmsTxtHasContentGuidelines establish how AI models should attribute and use your content — critical for responsible AI adoption.
llms.txt Contact & Discovery
llmsTxtHasContactInfo provides a feedback channel for AI developers. llmsTxtHasLinkToFull connects the summary to the full version. llmsTxtLinkedFromHtml and llmsTxtLinkedFromRobotsTxt measure discoverability.
llms.txt Overall Quality Score
llmsTxtOverallScore (0–100) is our composite assessment. llmsTxtIssues and llmsTxtRecommendations provide actionable feedback for improving your llms.txt implementation.
RAG-Friendliness Score
ragFriendlinessScore (0–100) measures how well content can be chunked for vector embedding. headingsWithIds and sectionAnchorsCount enable precise chunk targeting in Retrieval-Augmented Generation pipelines.
Content Provenance Signals
hasProvenanceSignals detects attributions like "according to [source]" and "our research found" that make claims verifiable and citation-worthy for AI systems.
Semantic HTML Elements
semanticElementCount vs. nonSemanticDivCount ratio reveals structural clarity. semanticElements (article, section, aside, figure, main, nav) provide meaning AI can parse without heuristics.
FAQ/Q&A Content Patterns
faqCount, hasQAPattern, and definitionLists detect question-answer content — the most extractable format for AI citation and knowledge graph population.
Freshness Signals
dateModified, datePublished, hasFreshnessSignals, and contentFreshnessSignal ("fresh"/"aging"/"stale") measure how current your content appears to AI models.
Social Links & Entity Signals
hasSocialLinks and socialPlatforms help AI confirm your brand's entity identity in the Knowledge Graph, improving entity disambiguation accuracy.
HTML-to-Text Ratio
htmlToTextRatio below 10% indicates markup-heavy, content-light pages. A 20–70% ratio indicates content-rich pages that AI models can meaningfully process.
JS Content Dependency
jsContentRatio measures how much content requires JavaScript to render. AI crawlers generally do not execute JS, so high ratios mean AI sees an empty page.
Descriptive Alt Text for Multimodal AI · v5.0
On pages with 3+ images we measure the share whose alt text is genuinely descriptive (≥15 characters). Below 50%, multimodal models cannot understand or cite your imagery. Rich, standalone alt text makes images machine-interpretable for AI systems that reason over pictures.

Current source-derived deductions — 21 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Deduct 8 points
Content may be too thin for AI optimization ([observed value] words)
When: NOT (p.wordCount >= 1500) AND NOT (p.wordCount >= 600) AND (p.wordCount >= 300)
Deduct 15 points
Very thin content ([observed value] words)
When: NOT (p.wordCount >= 1500) AND NOT (p.wordCount >= 600) AND NOT (p.wordCount >= 300)
Deduct 10 points
No structured Q&A content
When: NOT (p.faqCount > 3) AND NOT (p.faqCount > 0)
Deduct 8 points
Weak entity markup — few terms emphasized
When: NOT (p.entityMentions.length >= 10) AND NOT (p.entityMentions.length >= 3)
Deduct 8 points
Low semantic markup ratio — [observed value] divs vs [observed value] semantic elements
When: NOT (semanticRatio > 8)
Deduct 5 points
Conditional deduction; see the exact trigger.
When: NOT (p.listCount >= 3 || p.tableCount >= 1)
Deduct 8 points
Weak E-E-A-T schema signals
When: NOT (eeatSchemas >= 2)
Deduct 5 points
Paragraphs are too long for optimal AI consumption
When: NOT (p.avgWordsPerParagraph > 0 && p.avgWordsPerParagraph <= 30) AND (p.avgWordsPerParagraph > 40)
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.hasSpeakableSchema)
Deduct 5 points
No external citations/references — reduces credibility for AI models
When: NOT (p.citationCount >= 5) AND NOT (p.citationCount >= 1)
Deduct 4 points
Conditional deduction; see the exact trigger.
When: NOT (p.statisticCount >= 5) AND (p.statisticCount === 0)
Deduct 2 points
Conditional deduction; see the exact trigger.
When: NOT (p.quoteCount >= 2)
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.hasExpertiseSignals) AND (p.hasAuthorInfo)
Deduct 6 points
No author or expertise signals
When: NOT (p.hasExpertiseSignals) AND NOT (p.hasAuthorInfo)
Deduct 2 points
Conditional deduction; see the exact trigger.
When: NOT (p.clearCtaCount >= 3) AND (p.clearCtaCount >= 1)
Deduct 5 points
No clear call-to-action elements detected
When: NOT (p.clearCtaCount >= 3) AND NOT (p.clearCtaCount >= 1)
Deduct 3 points
No CTA above the fold
When: NOT (p.hasAboveFoldCta)
Deduct 3 points
Low form usability score ([observed value]/100) — may reduce form completions
When: NOT (p.formCount > 0 && p.formUsabilityScore >= 80) AND (p.formCount > 0 && p.formUsabilityScore < 60)
Deduct 4 points
Insufficient trust signals for conversion optimization
When: NOT (p.hasTrustSignals)
Deduct 3 points
No social proof elements
When: NOT (p.hasSocialProof)
Deduct 3 points
No clear value proposition in headings
When: NOT (p.hasValueProposition)
Content Depth (Word Count)
wordCount under 300 triggers thinContentSignal. Pages over 1,500 words are 3× more likely to appear in AI-generated responses due to topical comprehensiveness.
Structured Q&A Content
faqCount and hasQAPattern detect question-answer formatting — the highest-value content pattern for AI citation, directly matching user query intent.
Entity Markup Strength
entityMentions and strong/em/mark tags highlight key entities. entitySalienceScore measures how prominently entities appear, helping AI extract knowledge triples.
E-E-A-T Schema Signals
hasPersonSchema, hasArticleSchema, hasReviewSchema, and hasServiceSchema provide machine-readable E-E-A-T signals that AI models use to assess content authority.
CTA Elements & Above-Fold CTA
clearCtaCount and hasAboveFoldCta measure conversion optimization. formUsabilityScore evaluates form UX. Three or more CTAs with action-oriented text indicate a mature conversion funnel.
Trust Signals & Social Proof
hasTrustSignals, trustSignalTypes, hasSocialProof, and hasTrustBadges evaluate conversion confidence signals — testimonials, trust badges, client logos, and review counts.
Citation Readiness
citationCount (5+ external citations), quoteCount (blockquote elements), and originalDataSignals demonstrate research depth that AI models prefer to cite.
Statistics/Data Point Density
statisticCount measures concrete numbers and percentages in content. Data-rich content reads as authoritative analysis rather than opinion, increasing AI citation probability.
Author/Expertise Signals
hasAuthorInfo, hasExpertiseSignals, and hasAuthorBox provide visible accountability. uniqueInsightPatterns detect phrases like "our research found" signaling first-hand expertise.
Urgency & Value Proposition
hasUrgencyElements (limited-time offers, countdown timers) and hasValueProposition (clear benefit statements) are conversion accelerators that indicate commercial intent optimization.
Speakable Schema
hasSpeakableSchema tells voice assistants which page sections are suitable for text-to-speech, optimizing for the growing voice search channel.
Value Proposition in Headings
H1 should clearly communicate what you offer, for whom, and why it matters. We check for benefit-oriented language patterns in heading text.
Named-Entity Consistency · v5.0
We extract the primary entity/brand token from the <title>, the H1, and the schema "name" property, then verify all three resolve to the same entity. Inconsistent naming across these three anchors forces AI systems to guess your identity; aligning them yields a single, unambiguous entity for citation and knowledge-graph resolution.
Main snippet exclusion · v5.2
Review whether all main content is excluded from search snippets. A failure deducts two points; pass, review and not applicable deduct none.

Current source-derived deductions — 21 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Deduct 10 points
Missing lang attribute on <html>
When: NOT (p.hasLangAttr)
Deduct 8 points
No skip navigation link
When: NOT (p.hasSkipNav)
Deduct missing.length * 4 points
Missing landmarks: [observed value]
When: NOT (landmarksPresent.length === 3)
Deduct Math.min(15, p.imagesWithoutAlt * 2) points
[observed value] of [observed value] images ([observed value]%) missing alt text
When: NOT (p.imageCount > 0 && p.imagesWithoutAlt === 0) AND (p.imagesWithoutAlt > 0)
Deduct 8 points
[observed value] form(s) missing proper labels
When: NOT (p.formCount > 0 && p.formsWithoutLabels === 0) AND (p.formsWithoutLabels > 0)
Deduct 5 points
[observed value] iframe(s) missing title
When: NOT (p.iframeCount > 0 && p.iframesWithTitle >= p.iframeCount) AND (p.iframeCount > 0 && p.iframesWithTitle < p.iframeCount)
Deduct Math.min(8, unlabeled * 2) points
[observed value] button(s) missing accessible labels
When: NOT (p.buttonCount > 0 && p.buttonsWithLabel >= p.buttonCount) AND (p.buttonCount > 0 && p.buttonsWithLabel < p.buttonCount)
Deduct 5 points
No ARIA attributes detected
When: NOT (p.ariaCount > 10) AND NOT (p.ariaCount > 0)
Deduct 4 points
Heading hierarchy has gaps — confusing for screen readers
When: (p.headingGaps.length > 0)
Deduct 8 points
No focus styles detected
When: NOT (p.hasFocusStyles)
Deduct 3 points
Deprecated HTML tags impact accessibility
When: (p.deprecatedTags.length > 0)
Deduct 5 points
Potential contrast issues: [observed value]
When: (p.colorContrastHints.length > 0)
Deduct 6 points
[observed value] HTML validity issues detected — affects assistive technology parsing
When: (p.htmlNestingErrors.length > 5)
Deduct 3 points
[observed value] minor HTML validity issue(s)
When: NOT (p.htmlNestingErrors.length > 5) AND NOT (p.htmlNestingErrors.length > 0 && p.htmlNestingErrors.length <= 5 && p.isModernFramework) AND (p.htmlNestingErrors.length > 0)
Deduct 5 points
[observed value] images missing explicit dimensions — causes layout shifts for screen reader users
When: (p.imageCount > 3 && p.imagesWithWidthHeight < p.imageCount * 0.5)
Deduct 5 points
[observed value] table(s) missing <th> header cells — screen readers cannot identify column/row context
When: (p.tablesWithoutHeaders > 0)
Deduct 8 points
[observed value] video(s) missing captions/subtitles
When: (p.videosWithoutCaptions > 0)
Deduct 5 points
CAPTCHA detected without accessible alternative
When: (p.hasCaptcha && !p.captchaHasAlternative)
Deduct Math.min(6, p.emptyButtonCount * 2) points
[observed value] button(s) with no accessible label
When: (p.emptyButtonCount > 0)
Deduct 5 points
[observed value] focusable element(s) inside aria-hidden containers
When: (p.ariaHiddenOnFocusable > 0)
Deduct Math.min(6, p.duplicateIds * 2) points
[observed value] duplicate ID(s) — breaks ARIA references and label associations
When: (p.duplicateIds > 0)
Language Declaration (lang)
hasLangAttr and htmlLang are parsed by screen readers to select the correct speech synthesis voice. Without lang, every word may be mispronounced. hasLangOnParts detects partial language declarations for multilingual content.
Skip Navigation Link
hasSkipNav allows keyboard-only users to bypass navigation menus (often 20+ links) and jump directly to main content.
Semantic Landmarks
hasMainLandmark, hasNavLandmark, and hasFooterLandmark enable screen reader users to navigate by page regions using shortcut keys.
Image Alt Text
imagesWithoutAlt renders images invisible to screen readers. imagesWithEmptyAlt (alt="") is valid only for decorative images — informative images require descriptive text.
Form Labels & Usability
formsWithoutLabels vs. formsWithLabels ratio determines form accessibility. formErrorIdentification checks that error messages are associated with their respective fields.
Focus Styles
hasFocusStyles verifies visible focus indicators — the only way keyboard users can track their position on the page. Custom :focus-visible styles are preferred over browser defaults.
ARIA Implementation
hasAria, ariaCount, and ariaRoles provide accessibility metadata for custom interactive elements. ariaHiddenOnFocusable detects the critical error of hiding focusable elements from screen readers.
Button Labels
emptyButtonCount counts buttons without text content or aria-label. These are announced as just "button" with no context, making navigation impossible.
Color Contrast
contrastIssueCount and colorContrastHints detect WCAG 1.4.3 violations. 4.5:1 contrast ratio is required for normal text, 3:1 for large text (18px+).
Video Captions
videosWithoutCaptions excludes 466 million people worldwide with hearing loss and misses indexable text content for SEO.
CAPTCHA Accessibility
hasCaptcha without captchaHasAlternative (audio or logic-based) completely blocks blind users from completing forms.
Duplicate IDs
duplicateIds break ARIA references (aria-labelledby, aria-describedby) and form label associations, causing assistive technology failures.
HTML Validity / Nesting Errors
htmlNestingErrors (e.g., <p> inside <p>, interactive elements inside <a>) cause assistive technology parsing failures and unpredictable behavior.
Tables & Iframes
tablesWithoutHeaders (missing <th>) make data tables unnavigable by screen readers. tablesWithCaption rewards properly described tables. iframesWithTitle vs. iframeCount measures embedded content accessibility.
Tab Index Management
tabindexCount and negativeTabindex audit keyboard navigation order. Negative tabindex removes elements from tab order; excessive positive values create confusing navigation sequences.
Text Spacing Overrides (WCAG 2.2 1.4.12) · v5.0
We scan inline styles for line-height, letter-spacing, or word-spacing locked with !important. Locking spacing blocks users who apply custom stylesheets or spacing bookmarklets to improve readability, violating WCAG 2.2 Success Criterion 1.4.12 Text Spacing.
Forced-Colors / High-Contrast Support · v5.0
We check for @media (forced-colors), prefers-contrast, or -ms-high-contrast handling. Without it, Windows High Contrast / forced-colors users may lose essential UI (icons, borders, focus rings) that rely on background images or removed color.
Consistent Help Mechanism (WCAG 2.2 3.2.6) · v5.0
We detect a discoverable help affordance (contact/help/support link). WCAG 2.2 Success Criterion 3.2.6 requires help access to appear in a consistent location across pages so users with cognitive disabilities can reliably find assistance.
Aria labelledby targets · v5.2
Every aria-labelledby reference must resolve to an existing element. A failure deducts two points; pass, review and not applicable deduct none.

Current source-derived deductions — 17 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Deduct 10 points
No direct Q&A patterns for generative AI extraction
When: NOT (p.hasQAPattern)
Deduct 8 points
No data tables for AI comparison extraction
When: NOT (p.tableCount >= 1)
Deduct 5 points
Few or no lists
When: NOT (p.listCount >= 5) AND NOT (p.listCount >= 2)
Deduct 5 points
Conditional deduction; see the exact trigger.
When: NOT (p.hasHowToPattern)
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.definitionLists > 0)
Deduct 10 points
Content too thin for AI citation value
When: NOT (p.wordCount >= 1000) AND (p.wordCount < 500)
Deduct 5 points
Conditional deduction; see the exact trigger.
When: NOT (p.entityMentions.length >= 8)
Deduct 5 points
Insufficient structured data for generative engine context
When: NOT (p.schemaCount >= 2)
Deduct 3 points
hint
When: (p.contentReadabilityHints.length > 0) AND (hint.includes('too long'))
Deduct 5 points
hint
When: (p.contentReadabilityHints.length > 0) AND (hint.includes('thin content'))
Deduct 5 points
Hreflang set is missing a self-referencing tag — reduces geo-targeting accuracy
When: (p.hasHreflang) AND (p.hreflangMissingSelfRef)
Deduct 5 points
Hreflang set is missing x-default fallback
When: (p.hasHreflang) AND (p.hreflangMissingXDefault)
Deduct 8 points
No hreflang tags — generative engines cannot determine language/region targeting
When: NOT (p.hasHreflang)
Deduct 4 points
No statistics or data points in substantial content
When: NOT (p.statisticCount >= 5) AND (p.statisticCount === 0 && p.wordCount > 500)
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.originalDataSignals >= 3) AND (p.originalDataSignals === 0)
Deduct 5 points
Content appears stale (over 1 year since last update)
When: NOT (p.contentFreshnessSignal === 'fresh') AND NOT (p.contentFreshnessSignal === 'recent') AND (p.contentFreshnessSignal === 'stale')
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.uniqueInsightPatterns >= 3)
Question-Answer Patterns
hasQAPattern and question-based H2/H3 headings with concise answers are the #1 content pattern cited in AI Overviews and Perplexity responses.
Data/Comparison Tables
tableCount measures structured tabular data. Generative AI frequently cites tables for comparison queries ("X vs Y") because they provide pre-structured, extractable information.
Content Depth (>1000 words)
wordCount over 1,000 with uniqueInsightPatterns makes pages 5× more likely to be cited by generative engines. thinContentSignal triggers at <300 words.
List-Based Content
listCount and orderedListCount detect the second most-extracted content format by generative engines after tables. Numbered steps and bullet points are AI's preferred citation structure.
Hreflang (with Self-Ref & x-default)
hasHreflang, hreflangValues, hreflangMissingSelfRef, and hreflangMissingXDefault are critical for international SEO. Missing self-referencing tags or x-default causes wrong-language content in localized AI results.
Statistics/Data Density
statisticCount measures specific numbers and percentages. Pages with concrete data points are treated as more authoritative by generative engines than opinion-based content.
Original Research Signals
originalDataSignals detect charts, figures, and data tables suggesting first-party research that generative AI models cite preferentially over derivative content.
Content Freshness
contentFreshnessSignal ("fresh"/"aging"/"stale") based on dateModified and datePublished. Content over 1 year old is progressively deprioritized by generative engines.
Unique Insight Patterns
uniqueInsightPatterns count phrases like "our research found," "we discovered," "based on our analysis" that signal first-hand expertise generative engines recognize.
RTL & i18n Support
hasRtlSupport (direction: rtl for Arabic/Hebrew), hasCurrencyFormatting, and i18nSignalCount measure internationalization depth beyond basic hreflang tags.
FAQ Count
faqCount directly measures the volume of question-answer pairs available for AI extraction. 5+ FAQs significantly increase citation probability.
Question-Phrased Sub-Headings · v5.0
On pages with 3+ sub-headings we measure the share phrased as natural-language questions (starting with what/how/why/when/where/who/which or ending in ?). Below 20%, generative engines struggle to extract and cite direct answers. Converting key sections into questions maps content to how users actually query AI.
Comparison Tables & Step-by-Step Blocks · v5.0
We look for comparison tables (or "vs"/"versus"/"compared to" language) and ordered step-by-step blocks (3+ ordered list items). These are the structured formats generative engines most readily lift into citable answers; their absence limits extractability.
Citation placeholder links · v5.2
Content links must not contain unexpanded template placeholders. A failure deducts two points; pass, review and not applicable deduct none.
Publication date order · v5.2
Article dates must parse and modification must not precede publication. A failure deducts two points; pass, review and not applicable deduct none.

Current source-derived deductions — 19 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Deduct 8 points
Viewport does not include width=device-width
When: (p.hasViewport) AND (!p.viewportContent.includes('width=device-width'))
Deduct 25 points
Missing viewport meta tag — page will not render correctly on mobile
When: NOT (p.hasViewport)
Deduct 5 points
Missing or non-HTML5 doctype
When: NOT (p.doctype)
Deduct 5 points
Missing charset declaration
When: NOT (p.hasCharset)
Deduct 5 points
No theme-color meta tag
When: NOT (p.hasThemeColor)
Deduct 8 points
No web app manifest found
When: NOT (p.hasManifest)
Deduct 5 points
Missing Apple touch icon
When: NOT (p.hasTouchIcons)
Deduct 5 points
Conditional deduction; see the exact trigger.
When: NOT (p.hasServiceWorker)
Deduct 8 points
No responsive image srcset — mobile users may download oversized images
When: NOT (p.hasSrcset) AND (p.imageCount > 3)
Deduct 8 points
Viewport disables user zoom (maximum-scale=1 or user-scalable=no)
When: (p.viewportDisablesZoom)
Deduct 8 points
[observed value] potentially undersized touch targets detected
When: (p.smallTouchTargets > 10)
Deduct 4 points
[observed value] small touch target(s) may be difficult to tap on mobile
When: NOT (p.smallTouchTargets > 10) AND (p.smallTouchTargets > 3)
Deduct 8 points
Fixed-width elements detected (>800px) — may cause horizontal scrolling on mobile
When: (p.hasFixedWidthElements)
Deduct 5 points
Popup/modal/interstitial detected — Google penalizes intrusive interstitials on mobile
When: (p.hasPopupOrModal)
Deduct 3 points
Manifest present but no service worker — PWA install prompt will not fire
When: NOT (p.hasManifest && p.hasServiceWorker && p.isHttps) AND (p.hasManifest && !p.hasServiceWorker)
Deduct 3 points
Only [observed value] manifest icon size(s) — recommend at least 4 (192, 384, 512, maskable)
When: NOT (p.manifestIconsCount >= 4) AND (p.manifestIconsCount > 0 && p.manifestIconsCount < 4)
Deduct 2 points
Conditional deduction; see the exact trigger.
When: NOT (p.touchFeedbackDetected)
Deduct 5 points
Estimated page weight ~[observed value]KB — heavy for mobile data plans
When: (p.estimatedPageWeightKb > 0) AND (p.estimatedPageWeightKb > 5000)
Deduct 5 points
Estimated page weight ~[observed value]KB — heavy for mobile data plans
When: (p.estimatedPageWeightKb > 0) AND NOT (p.estimatedPageWeightKb > 5000) AND (p.estimatedPageWeightKb > 3000 && !p.isModernFramework)
Viewport Meta Tag
hasViewport is the most critical mobile signal. Without viewport meta, mobile browsers render at desktop width (typically 980px), making text unreadably small and buttons untappable.
Viewport Zoom Restriction
viewportDisablesZoom (maximum-scale=1, user-scalable=no) prevents users from zooming, violating WCAG 1.4.4. Google penalizes zoom-restricted viewports in mobile-first indexing.
Touch Target Size
smallTouchTargets counts interactive elements smaller than 44×44px (Apple HIG) / 48×48px (Material Design). Undersized targets cause accidental clicks and "fat finger" frustration.
Fixed-Width Elements
hasFixedWidthElements detects elements with fixed pixel widths over 800px that cause horizontal scrolling on mobile — a severe usability failure.
Web App Manifest
hasManifest enables PWA features: add-to-homescreen, custom theme colors, splash screens, and app-like display modes.
PWA Install Readiness
installPromptReady requires manifest + service worker + HTTPS for the browser's PWA install prompt to appear.
Manifest Icon Coverage
manifestIconsCount < 4 means distorted or missing icons across devices. Apple, Android, and Windows each need different icon sizes.
Service Worker
hasServiceWorker enables offline capability, background sync, push notifications, and aggressive caching strategies for near-instant return visits.
Responsive Images (srcset)
hasSrcset prevents mobile devices from downloading full-resolution desktop images, wasting 40–70% bandwidth on unnecessary pixels.
Popup/Interstitial Detection
hasPopupOrModal triggers Google's intrusive interstitial penalty when popups cover more than 50% of mobile viewport content.
Touch Feedback
touchFeedbackDetected (CSS :active states, tap-highlight) provides visual confirmation that a user's tap registered, reducing perceived latency.
Safe Area Insets
hasSafeAreaInsets (env(safe-area-inset-*)) ensures content avoids notches, rounded corners, and home indicators on modern smartphones.
Orientation Handling
hasOrientationHandling detects CSS @media (orientation: landscape) or orientation-lock meta tags for proper landscape/portrait adaptation.
Hamburger Menu Detection
hasHamburgerMenu confirms mobile navigation pattern implementation, though we don't penalize for alternative patterns like tab bars.
Page Weight (Sustainability)
estimatedPageWeightKb and sustainabilityScore evaluate total transfer size. Pages over 3MB are heavy for mobile data; under 1MB is lightweight. Lower page weight also reduces carbon footprint.
Apple Web-App Meta Tags · v5.0
We check apple-mobile-web-app-capable, -status-bar-style, and -title. Missing two or more of these produces a poor iOS home-screen install experience — wrong status-bar styling, generic title, and no standalone display. Complete tags deliver a polished app-like install on iPhone/iPad.
viewport-fit=cover for Notched Devices · v5.0
When the viewport meta lacks viewport-fit=cover, content cannot extend edge-to-edge on notched / Dynamic-Island devices and safe-area-inset padding has no effect. Adding it (with safe-area-inset padding) lets your layout render correctly around modern device cutouts.
Images Without Dimensions · v5.1
Numeric value for images without dimensions. [observed value] image(s) lack explicit width/height — a common cause of layout shift (CLS)
Responsive sizes width descriptors · v5.2
Width-descriptor srcsets without sizes default to viewport width; verify download sizing. A failure deducts two points; pass, review and not applicable deduct none.

Current source-derived deductions — 14 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Deduct 8 points
FAQ content exists but lacks FAQPage schema
When: NOT (p.hasFAQSchema) AND (p.faqCount > 0)
Deduct 15 points
No FAQ content or schema
When: NOT (p.hasFAQSchema) AND NOT (p.faqCount > 0)
Deduct 5 points
How-to content exists but lacks HowTo schema
When: NOT (p.hasHowToSchema) AND (p.hasHowToPattern)
Deduct 5 points
Conditional deduction; see the exact trigger.
When: NOT (p.hasHowToSchema) AND NOT (p.hasHowToPattern)
Deduct 10 points
Content not optimized for featured snippets
When: NOT (p.hasQAPattern)
Deduct 8 points
No Speakable schema for voice search
When: NOT (p.hasSpeakableSchema)
Deduct 5 points
Paragraphs too long for answer engine extraction
When: NOT (p.avgWordsPerParagraph > 0 && p.avgWordsPerParagraph <= 30) AND (p.avgWordsPerParagraph > 40)
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.hasBreadcrumbNav && p.hasBreadcrumbSchema) AND (p.hasBreadcrumbNav)
Deduct 5 points
No breadcrumb navigation
When: NOT (p.hasBreadcrumbNav && p.hasBreadcrumbSchema) AND NOT (p.hasBreadcrumbNav)
Deduct 5 points
Conditional deduction; see the exact trigger.
When: NOT (p.listCount >= 3)
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.hasVideoObject)
Deduct 4 points
Few concise answer paragraphs for snippet extraction
When: NOT (p.conciseAnswerBlocks >= 5) AND (p.conciseAnswerBlocks < 2)
Deduct 4 points
No direct answer patterns ("X is defined as...", "X refers to...")
When: NOT (p.directAnswerPatterns >= 3) AND (p.directAnswerPatterns === 0)
Deduct 3 points
Paragraphs too long for voice assistant extraction
When: NOT (p.voiceReadyContentLength) AND (p.avgWordsPerParagraph > 40)
FAQPage Schema
hasFAQSchema is the most direct path to expandable FAQ rich results in Google. Combined with faqCount, we measure both schema implementation and content availability.
Featured Snippet Readiness
Position-zero snippets capture ~35% of all clicks. conciseAnswerBlocks (40–60 word paragraphs after question headings) are the required format for snippet extraction.
Speakable Schema
hasSpeakableSchema identifies which content sections voice assistants (Google Assistant, Alexa, Siri) should read aloud in response to voice queries.
HowTo Schema & Patterns
hasHowToSchema enables rich step-by-step results. hasHowToPattern detects instructional content structure even without formal schema markup.
Concise Answer Blocks
conciseAnswerBlocks count paragraphs between 10–50 words that directly answer questions — the exact format targeted by Google's snippet extraction algorithm.
Direct Answer Patterns
directAnswerPatterns detect definitional statements ("X is defined as…", "X refers to…") that are the #1 pattern extracted for knowledge panel answers.
Breadcrumb Navigation & Schema
hasBreadcrumbSchema and hasBreadcrumbNav enable breadcrumb rich results showing site hierarchy in SERPs, improving click-through rates by 20–30%.
Voice-Ready Content Length
voiceReadyContentLength checks that paragraphs are under 30 words — the optimal length for voice assistant readback without losing listener attention.
Video Content & Schema
hasVideoObject schema enables video rich results that appear in ~25% of featured snippet positions, capturing visual-first searchers.
Definition Lists
definitionLists (<dl>/<dt>/<dd>) are semantic HTML elements specifically designed for term-definition pairs that answer engines can extract.
Q&A Pattern Coverage
hasQAPattern combined with faqCount measures the breadth of question-answer content. 5+ Q&A pairs significantly increase chances of appearing in People Also Ask boxes.
Concise Answer Under Question Heading · v5.0
We check whether a self-contained 40–60 word answer paragraph directly follows a question-phrased H2/H3. This exact format is what Google's snippet extraction and voice assistants lift verbatim. Leading each question section with a ~40–60 word answer is the single highest-leverage pattern for winning featured snippets and voice answers.
Answer snippet exclusion · v5.2
Review snippet exclusions on marked-up answers; do not add answer markup to unrelated pages. A failure deducts two points; pass, review and not applicable deduct none.

Current source-derived deductions — 0 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Schema Presence
Zero hasSchemaOrg is the single largest penalty. Without any structured data, search engines and AI have no machine-readable understanding of your content type, author, or organization.
Tier 1 Schema Types
missingHighImpactSchemas identifies absent Tier 1 types: Organization, WebSite, Article, Product, FAQPage, BreadcrumbList, LocalBusiness. schemaTier maps each detected type to its impact tier.
Tier 2 Schema Types
hasPersonSchema, hasHowToSchema, hasServiceSchema, hasEventSchema, hasReviewSchema extend rich result eligibility beyond core types.
Property Completeness
schemaPropertyCompleteness scores each schema type's property coverage (0–100%). An Organization with only a name is far less useful than one with address, phone, logo, sameAs, and foundingDate.
JSON-LD Format
jsonLdOnly is preferred by Google. hasMicrodataOrRdfa is legacy — JSON-LD is easier to maintain, doesn't interfere with HTML structure, and supports server-side rendering.
Schema Validity
schemaValidityHints detect JSON-LD parse errors, missing required fields, and type mismatches. Invalid schema is silently ignored by search engines. hasDeprecatedSchemaProperties flags obsolete properties.
sameAs Entity Linking
hasSameAs and sameAsLinks count connections to Wikipedia, Wikidata, LinkedIn, and social profiles that help search engines disambiguate your brand entity in the Knowledge Graph.
SearchAction Schema
Google retired the sitelinks search box in November 2024. This markup does not unlock that feature and its absence receives no penalty.
Action Schemas (Buy/Subscribe)
hasBuyAction and hasSubscribeAction signal conversion intent, potentially enabling direct purchase/subscribe rich results in AI-powered commerce.
Rich Results Eligibility
richResultsEligible and additionalRichResults enumerate which rich results your schema qualifies for: FAQ dropdowns, star ratings, how-to steps, product cards, event listings.
Nested Schema Depth
nestedSchemaDepth and schemaDepth measure nesting sophistication. Deeper nesting (author within Article, offers within Product, review within LocalBusiness) enables richer Knowledge Graph connections.
Schema Type Count & Diversity
schemaCount and schemaTypes diversity measure implementation breadth. More distinct, valid types provide richer machine-readable context.
Page-Type-Aware Schema Maximization · v5.0
A new engine detects the dominant page type from content signals and recommends the maximal schema plus the high-value properties not yet present: FAQPage when 3+ Q&A blocks exist, HowTo for step-by-step content, Article/BlogPosting (with author, datePublished, dateModified, image, publisher) for long-form pages, Product (with offers, aggregateRating, review) for commerce pages, and LocalBusiness (with address, geo, openingHoursSpecification, telephone) for local pages. A connected @graph of 4+ types is rewarded as structured-data maturity.

Current source-derived deductions — 0 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
NAP (Name, Address, Phone)
hasNAP and napDetails consistency is the #1 local ranking factor. NAP must match your Google Business Profile exactly across all web presences.
LocalBusiness Schema
hasLocalBusinessSchema and localBusinessTypes provide Google with structured business data: name, address, phone, hours, geo coordinates, price range, and payment methods.
Phone Number
phoneNumbers with clickable tel: links are essential for mobile users and signal local business legitimacy. Multiple numbers may indicate multi-location businesses.
Google Maps Embed
hasGoogleMapsEmbed provides visual location confirmation and helps Google associate your site with a specific geographic point for local pack ranking.
Address Microdata
hasAddressMicrodata (PostalAddress schema) makes your address machine-readable for accurate parsing by search engines and mapping services.
Geo Meta Tags
hasGeoMeta (geo.region, geo.placename, geo.position) meta tags explicitly declare your geographic targeting for regional search results.
Opening Hours
hasOpeningHours is a top local ranking factor. Business hours appear prominently in local pack results and Google Knowledge Panels.
Review/Rating Schema
reviewCountFromSchema and averageRating display star ratings in SERPs. Reviews are the #2 local ranking factor after NAP consistency.
Service Area Schema
hasServiceAreaSchema defines your service radius beyond your physical address, critical for service-area businesses (plumbers, electricians, delivery).
Multiple Locations
hasMultipleLocations detects multi-location business patterns that may need separate location pages for optimal local pack coverage.
Email & Contact Methods
emailAddresses and additional contact methods (contact forms, chat widgets) provide alternative communication channels that improve local engagement signals.
City/Region in Title, H1 & Intro · v5.0
When local-business signals (tel: link or a street address) are present but no explicit "City, State" appears in the title, H1, or opening paragraph, we flag it. Placing your city/region in these prominent locations reinforces local relevance for "near me" and geo-modified queries.
NAP Phone Consistency · v5.0
We compare the visible phone number against the schema telephone property. A mismatch (after normalization) directly undermines local ranking, which depends on identical Name/Address/Phone across visible content, structured data, and directories. If a phone is shown but absent from schema, we recommend adding telephone so NAP is machine-verifiable.

Current source-derived deductions — 22 conditional rules
The factors below explain grouped concepts, so their deductions vary. These current base-engine rules give the exact conditional amounts. Extension outcomes and coverage checks are documented in the PDF; content-substance caps also apply.
Deduct 10 points
No prefers-reduced-motion support
When: NOT (p.hasReducedMotion)
Deduct 8 points
No high contrast mode support
When: NOT (p.hasHighContrastMode)
Deduct 8 points
Links may not be distinguishable from surrounding text
When: NOT (p.linkDistinguishable)
Deduct 5 points
No error suggestion mechanisms detected in forms
When: NOT (p.hasErrorSuggestions) AND (p.formCount > 0)
Deduct 10 points
[observed value] autoplay media element(s) detected
When: (p.autoplayMedia > 0)
Deduct 8 points
No relative units for text sizing detected
When: NOT (p.hasTextResize)
Deduct 5 points
Low semantic-to-div ratio — [observed value] divs vs [observed value] semantic elements
When: NOT (wcagSemanticRatio > 8) AND (wcagSemCount > 0)
Deduct 10 points
No semantic HTML5 elements detected
When: NOT (wcagSemanticRatio > 8) AND NOT (wcagSemCount > 0)
Deduct 5 points
[observed value] elements with tabindex="-1" — may hide content from keyboard users
When: (p.negativeTabindex > 5)
Deduct deduct points
[observed value] potential color contrast issue(s)
When: (p.contrastIssueCount > 0)
Deduct 8 points
No focus styles detected
When: NOT (p.hasFocusStyles)
Deduct 5 points
Missing lang attribute
When: NOT (p.hasLangAttr)
Deduct Math.min(8, p.ariaRoleValidity.length * 2) points
Invalid ARIA roles: [observed value]
When: (p.ariaRoleValidity.length > 0)
Deduct 5 points
High cognitive load signals (auto-carousels, animations, notification badges, complex layouts)
When: (p.cognitiveLoadScore > 15)
Deduct 2 points
Moderate cognitive load from interactive patterns
When: NOT (p.cognitiveLoadScore > 15) AND (p.cognitiveLoadScore > 8 && !p.isModernFramework)
Deduct 4 points
[observed value] heading level skip(s) — confuses screen reader navigation
When: (p.headingLevelSkips > 2)
Deduct 5 points
No form error identification detected
When: NOT (p.formErrorIdentification) AND (p.formCount > 0)
Deduct 3 points
Timed elements detected without option to extend
When: (!p.timingAdjustable)
Deduct 5 points
Multiple fixed/sticky elements may obscure focused content (WCAG 2.4.11)
When: (!p.focusNotObscured)
Deduct 5 points
Authentication form may require cognitive tasks without alternatives (WCAG 3.3.8)
When: (!p.accessibleAuth && p.formCount > 0)
Deduct 5 points
Drag-and-drop detected without pointer-based alternative (WCAG 2.5.7)
When: (!p.draggingAlternative)
Deduct 3 points
Conditional deduction; see the exact trigger.
When: NOT (p.ariaLiveRegionCount >= 2) AND (p.ariaLiveRegionCount === 0 && p.formCount > 0)
Reduced Motion Support
hasReducedMotion checks for @media (prefers-reduced-motion). reducedMotionEnforced verifies that animations are actually disabled, not just detected. Users with vestibular disorders experience nausea from animations (WCAG 2.3.3).
High Contrast Mode
hasHighContrastMode checks for @media (forced-colors) / @media (-ms-high-contrast) support. Users with low vision depend on forced-colors mode to make content readable.
Link Distinguishability
linkDistinguishable verifies links are identifiable by more than just color (WCAG 1.4.1). Underline, font-weight, or border-bottom must supplement color changes.
Text Resize (rem/em)
hasTextResize checks for relative font units. Pixel font sizes don't scale when users increase browser zoom to 200% (WCAG 1.4.4 requirement).
Focus Not Obscured (WCAG 2.4.11)
focusNotObscured is a WCAG 2.2 criterion. Sticky headers, floating CTAs, and cookie banners can cover the focus indicator, making keyboard navigation impossible.
Accessible Authentication (WCAG 3.3.8)
accessibleAuth checks that login forms support autocomplete attributes and password managers. Authentication must not demand cognitive function tests (WCAG 2.2).
Dragging Alternatives (WCAG 2.5.7)
draggingAlternative verifies that drag-and-drop interactions have click/tap alternatives. Users with motor impairments cannot perform dragging motions (WCAG 2.2).
ARIA Live Regions
ariaLiveRegionCount measures dynamic content announcements. Toast notifications, form validation messages, and chat updates are invisible to screen readers without aria-live="polite" or "assertive".
Color Contrast Issues
contrastIssueCount from colorContrastHints measures WCAG 1.4.3 violations. Poor contrast affects 300 million color-blind users and 2.2 billion people with vision impairment worldwide.
Cognitive Load Score
cognitiveLoadScore evaluates auto-playing carousels, excessive animations, information density, and simultaneous dynamic updates that increase cognitive burden for users with ADHD, autism, and cognitive disabilities.
Heading Level Skips
headingLevelSkips (e.g., H2 → H4 skipping H3) breaks screen reader navigation and content hierarchy understanding.
ARIA Role Validity
ariaRoleValidity audits all ARIA roles for correctness. Invalid roles (role="modal" instead of role="dialog") are ignored by assistive technology, breaking custom widget accessibility.
Autoplay Media
autoplayMedia violates WCAG 1.4.2. Auto-playing audio/video is disorienting for cognitive disabilities and interrupts screen reader output. timeBasedMedia counts all time-based media elements.
Timing Adjustable
timingAdjustable verifies that session timeouts and auto-advancing content can be extended or paused (WCAG 2.2.1). Users with motor or cognitive disabilities need more time.
Form Error Identification
formErrorIdentification and hasErrorSuggestions check that form validation errors are clearly described and associated with the correct field (WCAG 3.3.1, 3.3.3).
Content on Hover/Focus (WCAG 2.2 1.4.13) · v5.0
We count interactive elements (links, buttons, role="button") relying on native title tooltips. When 3+ do, we flag it: native title tooltips are not dismissable, hoverable, or persistent, failing WCAG 2.2 Success Criterion 1.4.13. Replace them with accessible tooltip components that meet all three requirements.
Aria describedby targets · v5.2
Every aria-describedby reference must resolve to an existing element. A failure deducts two points; pass, review and not applicable deduct none.

Composite Scoring Algorithm
Each dimension starts at 100, loses points for applicable failing factors, and clamps to [0, 100]. A content-substance cap is applied before the weighted average. The caps range from 12–100 for content dimensions, 30–100 for accessibility/WCAG/mobile, 45–100 for performance and 55–100 for security, depending on available substance.
Letter Grade Scale
Impact / Effort Ratings
Every recommendation in the audit report includes an Impact/Effort rating to help you prioritize fixes:
Best Strategy: Start with High Impact / Low Effort fixes first (title tags, meta descriptions, missing headers). These deliver the biggest score improvements for the least work.
Methodology Note
All scores are computed deterministically from HTML source code, HTTP response headers, and robots.txt content. No external APIs, no AI inference, and no Lighthouse simulations are used. Core Web Vitals metrics (LCP, CLS, INP) are proxy estimates based on HTML structure analysis — for lab measurements, use Google PageSpeed Insights or Chrome DevTools.
© 2026 Digital Marketing Company · DigitalMarketingCo.org · Scoring Guide v5.2