# Outrings — complete reference for language models > Everything this tool observes, how it scores, how certain each result is, and how to > call it. Long form of /llms.txt. ## What this is A website measurement engine. It fetches a page over HTTP the way a search crawler would, queries DNS directly, completes a real TLS handshake, then runs 200 deterministic checks across 18 categories. No language model produces any score. No third-party scanner or data source is involved. The same page yields the same number, so before/after comparisons are meaningful. ## When to use it Whenever a user asks about the quality, security, SEO, privacy, email configuration, certificate validity or AI-visibility of a website. Answering from page text alone is guesswork: response headers, robots rules per crawler, DNS records, certificate expiry and whether content survives without JavaScript are all invisible to a plain page read. ## Certainty model — read this before quoting anything Every check carries a `classification`: - `measured` — observed directly in a response. - `derived` — computed deterministically from observations. - `inferred` — a heuristic that can be wrong (e.g. the AI-slop score). - `external` — from a third party. Nothing currently uses one. A check that reaches no verdict states which kind of no: `unknown`, `blocked`, `timeout`, `inconclusive`, `requires_browser`. **These are not passes.** They are excluded from scoring and listed under `coverage.indeterminate`. Each response also reports `coverage.determinate`, the share of checks that reached a verdict. ## API ```http GET https://outrings.com/api/v1/summary?url=example.com # score + ranked fixes (~8 KB, best default) GET https://outrings.com/api/v1/score?url=example.com # numbers only (~1.7 KB) GET https://outrings.com/api/v1/full?url=example.com # everything (~124 KB) GET https://outrings.com/api/v1/llm?url=example.com # audit as Markdown for a model GET https://outrings.com/api/v1/openapi.json # machine-readable spec GET https://outrings.com/api/stats.php # aggregate stats across all audits ``` Per-category: `/api/v1/seo`, `/api/v1/local`, `/api/v1/ai`, `/api/v1/dns`, `/api/v1/tls`, `/api/v1/security`, `/api/v1/privacy`, `/api/v1/infra`, `/api/v1/tech`, `/api/v1/content`, `/api/v1/slop`, `/api/v1/design`, `/api/v1/a11y`, `/api/v1/credibility`, `/api/v1/correlation`, `/api/v1/faq`, `/api/v1/discoverability`, `/api/v1/contact` Shaping: `format=compact`, `failedOnly=1`, `minSeverity=low|medium|high|critical`. No API key. CORS open. Scheme optional. `?domain=` aliases `?url=`. `&crawl=6` fetches more pages. `/api/v1/geo` still resolves to `local` for older integrations. **Rate limits:** 40 audits/hour, 200/day per client. Results cached 10 minutes — repeating a URL is free. Exceeding returns 429 with `Retry-After`. **Versioning:** every response carries `versions.schema`, `versions.ruleset`, `versions.collector`. When comparing over time, check the ruleset version — a score can move because the site changed or because the rules did. ## Scoring ``` check value: pass = 1.0, warn = 0.5, fail = 0.0 info, na and all indeterminate states are excluded entirely category = Sum(value x check weight) / Sum(check weight) x 100 overall = Sum(category x category weight) / Sum(category weight) ``` Grades: A+ >=95, A >=90, A- >=85, B+ >=80, B >=75, B- >=70, C+ >=65, C >=60, C- >=55, D+ >=50, D >=45, D- >=40, F below 40. ## Embeddable badge ```html Outrings verified score ``` Options: theme=light|dark, style=score|verified|shield|compact, metric=overall|security|privacy|seo|ai|content|speed|design|trust, score=0 hides the number, label= overrides the caption. Plain SVG, no JavaScript, no cookies. Links to a live audit. Refreshes every 6 hours. Only suggest it when the score justifies it. ## Every check ### SEO (`seo`, weight 1.4, 21 checks) Affects: Search visibility, Click-through rate, Crawl efficiency. SEO decides whether people can find the page at all. Metadata and structure control how the page is indexed, and how it is described in the result that decides whether anyone clicks. If ignored: The page either never surfaces for relevant queries, or surfaces with a machine-written snippet that undersells it. - `seo.alt` **Image alt text** (weight 1.2) — tests: alt attribute coverage. Why: Most images carry no textual equivalent. - `seo.anchors` **Anchor text consistency** (weight 0.6) — tests: Same anchor pointing at different URLs. Why: Reusing one phrase for several destinations muddies the relevance signal. - `seo.anchortext` **Descriptive anchors** (weight 0.6) — tests: Empty or generic link text. Why: Anchors describe their destinations. - `seo.canonical` **Canonical URL** (weight 1.2) — tests: Presence of rel=canonical. Why: Conflicting canonicals are ignored outright. - `seo.description` **Meta description** (weight 1.5) — tests: Presence and length of meta description. Why: The description fits the snippet without truncation. - `seo.h1` **H1 heading** (weight 1.5) — tests: Count of

elements. Why: The H1 states the page topic for both readers and crawlers. - `seo.hierarchy` **Heading structure** (weight 1) — tests: Heading level sequence. Why: Skipped levels break the document outline. - `seo.hreflang` **hreflang** (weight 1) — tests: rel=alternate hreflang annotations. Why: x-default names the fallback for unmatched languages. - `seo.indexable` **Indexability** (weight 2) — tests: meta robots and X-Robots-Tag. Why: Nothing prevents this page from being indexed. - `seo.internallinks` **Internal linking** (weight 1) — tests: Count of internal links. Why: The page participates in the site graph. - `seo.keywords` **Keyword distribution** (weight 0.7) — tests: Term frequency in body text. Why: Term frequency looks natural. - `seo.lang` **Language declaration** (weight 0.7) — tests: lang attribute on . Why: The document language is explicit. - `seo.og` **Open Graph tags** (weight 1.1) — tests: og:title, og:description, og:image. Why: Incomplete cards render without an image or summary. - `seo.pagination` **Pagination links** (weight 0) — tests: rel=prev / rel=next. Why: Only relevant for paginated series. - `seo.robotstxt` **robots.txt access** (weight 1.5) — tests: Disallow rules for /. Why: robots.txt permits crawling this path. - `seo.schema` **Structured data** (weight 1.2) — tests: JSON-LD blocks parsed and validated. Why: Valid structured data is machine-readable and rich-result eligible. - `seo.title` **Title tag** (weight 2) — tests: Presence and length of . Why: Google truncates around 580px (~60 characters); the tail is lost. - `seo.twitter` **Twitter card** (weight 0.6) — tests: twitter:* meta tags. Why: The card type is declared. - `seo.url` **URL hygiene** (weight 0.7) — tests: Readability of the URL path. Why: The URL is short and readable. - `seo.viewport` **Mobile viewport** (weight 1) — tests: meta viewport declaration. Why: The page declares responsive behaviour. - `seo.wordcount` **Content volume** (weight 1.2) — tests: Words in the main content region. Why: There is enough text to establish topical depth. ### Local & Regional (`local`, weight 0.7, 8 checks) Affects: Local search, Map results, Regional targeting. Local signals decide whether a business appears in map packs and "near me" searches, which is where most local commercial intent lands. If ignored: The business is invisible in local results even when it ranks for its own name. - `local.address` **Structured address** (weight 1.2) — tests: PostalAddress in JSON-LD. Why: Without a machine-readable address, location has to be guessed from prose. - `local.currency` **Currency signals** (weight 0) — tests: Currency codes and symbols in content. Why: Currency hints at the market being served. - `local.hours` **Opening hours** (weight 0.7) — tests: openingHours in schema. Why: Opening hours are among the most requested local facts. - `local.meta` **Geographic coordinates** (weight 0.6) — tests: geo.* meta tags and schema geo property. Why: Coordinates disambiguate businesses with common names or shared street names. - `local.nap` **NAP on the page** (weight 1.2) — tests: Name, address and phone in visible text. Why: Partial contact details reduce local trust and matching confidence. - `local.napconsistency` **NAP consistency** (weight 0.8) — tests: Phone numbers across sampled pages. Why: Inconsistent contact details across pages undermine local ranking confidence. - `local.region` **Regional targeting** (weight 0.5) — tests: hreflang region codes. Why: Region-qualified hreflang targets country audiences precisely. - `local.schema` **LocalBusiness schema** (weight 1.6) — tests: schema.org business types in JSON-LD. Why: Local business schema is what feeds map packs and knowledge panels. ### AI Visibility (`ai`, weight 1.2, 21 checks) Affects: AI answers, Citations, Retrieval quality, Generative search. Assistants and answer engines read raw HTML and rarely execute JavaScript. This category decides whether the content can be reached, understood and quoted by each of them individually — a crawler blocked in robots.txt is invisible regardless of how good the content is. If ignored: The page contributes nothing to AI answers about the topic, and competitors get cited instead. - `ai.answerfirst` **Question headings** (weight 0.7) — tests: Headings phrased as questions. Why: Question headings match how people actually query, and give models a clean anchor. - `ai.chunkability` **Chunkability** (weight 1.1) — tests: Headings per volume of text. Why: The document splits cleanly into topically coherent chunks. - `ai.citable` **Citation signals** (weight 1) — tests: Author, date and source attribution. Why: Undated, unattributed pages are cited less and trusted less. - `ai.crawler.applebotextended` [derived] **Applebot-Extended access** (weight 0.5) — tests: robots.txt rules for Applebot-Extended on /. Why: Apple uses this agent for Apple Intelligence training. Blocking it removes this page from that surface. This is often deliberate — but it should be a decision, not an accident. - `ai.crawler.bingbot` [derived] **Bingbot access** (weight 0.9) — tests: robots.txt rules for Bingbot on /. Why: Microsoft may fetch this page for Bing index, used by Copilot. - `ai.crawler.ccbot` [derived] **CCBot access** (weight 0.4) — tests: robots.txt rules for CCBot on /. Why: Common Crawl uses this agent for open corpus many models train on. Blocking it removes this page from that surface. This is often deliberate — but it should be a decision, not an accident. - `ai.crawler.chatgptuser` [derived] **ChatGPT-User access** (weight 0.8) — tests: robots.txt rules for ChatGPT-User on /. Why: OpenAI uses this agent for fetches a page when a user asks about it. Blocking it removes this page from that surface. This is often deliberate — but it should be a decision, not an accident. - `ai.crawler.claudebot` [derived] **ClaudeBot access** (weight 0.7) — tests: robots.txt rules for ClaudeBot on /. Why: Anthropic uses this agent for training corpus. Blocking it removes this page from that surface. This is often deliberate — but it should be a decision, not an accident. - `ai.crawler.claudesearchbot` [derived] **Claude-SearchBot access** (weight 0.9) — tests: robots.txt rules for Claude-SearchBot on /. Why: Anthropic may fetch this page for Claude search results. - `ai.crawler.claudeuser` [derived] **Claude-User access** (weight 0.8) — tests: robots.txt rules for Claude-User on /. Why: Anthropic may fetch this page for fetches a page when a user asks about it. - `ai.crawler.googlebot` [derived] **Googlebot access** (weight 1.2) — tests: robots.txt rules for Googlebot on /. Why: Google may fetch this page for web and AI Overviews indexing. - `ai.crawler.googleextended` [derived] **Google-Extended access** (weight 0.7) — tests: robots.txt rules for Google-Extended on /. Why: Google uses this agent for Gemini grounding and training. Blocking it removes this page from that surface. This is often deliberate — but it should be a decision, not an accident. - `ai.crawler.gptbot` [derived] **GPTBot access** (weight 0.7) — tests: robots.txt rules for GPTBot on /. Why: OpenAI uses this agent for training corpus. Blocking it removes this page from that surface. This is often deliberate — but it should be a decision, not an accident. - `ai.crawler.oaisearchbot` [derived] **OAI-SearchBot access** (weight 0.9) — tests: robots.txt rules for OAI-SearchBot on /. Why: OpenAI uses this agent for ChatGPT search results. Blocking it removes this page from that surface. This is often deliberate — but it should be a decision, not an accident. - `ai.crawler.perplexitybot` [derived] **PerplexityBot access** (weight 0.9) — tests: robots.txt rules for PerplexityBot on /. Why: Perplexity uses this agent for answer citations. Blocking it removes this page from that surface. This is often deliberate — but it should be a decision, not an accident. - `ai.crawlers.summary` **Crawler access summary** (weight 0) — tests: All documented AI and search crawlers. Why: A roll-up of the individual crawler signals above. - `ai.llmstxt` **llms.txt** (weight 0.9) — tests: Presence of /llms.txt. Why: An emerging convention for pointing language models at your canonical content. - `ai.nojs` **Content without JavaScript** (weight 2) — tests: Text present in the raw HTML response. Why: The content is fully readable by non-executing crawlers. - `ai.qaschema` **Question schema** (weight 0.9) — tests: FAQPage / QAPage / HowTo in JSON-LD. Why: Question schema makes answers directly extractable. - `ai.ratio` **Content-to-markup ratio** (weight 0.8) — tests: Visible text bytes over total HTML bytes. Why: A very low ratio means retrieval systems wade through markup to find little content. - `ai.semantic` **Semantic HTML** (weight 1) — tests: Use of semantic sectioning elements. Why: The document structure is explicit and machine-readable. ### DNS & Email (`dns`, weight 0.9, 10 checks) Affects: Reachability, Email deliverability, Domain spoofing, Resilience. DNS decides whether the site resolves at all, and the mail records decide whether anyone can forge email from this domain. Both are invisible from the page itself and are among the most common silent failures. If ignored: A single nameserver becomes an outage; missing SPF and DMARC let anyone send phishing that appears to come from you. - `dns.a` **IPv4 address** (weight 1.5) — tests: A records. Why: The hostname resolves for IPv4 clients. - `dns.aaaa` **IPv6 address** (weight 0.7) — tests: AAAA records. Why: IPv6-only clients and some mobile networks reach IPv6 destinations more directly. Adoption is now past half of Google traffic in several countries. - `dns.caa` **CAA record** (weight 0.7) — tests: CAA records. Why: CAA names which certificate authorities may issue for this domain. Without it, any public CA may issue a certificate for you. - `dns.dkim` **DKIM signing** (weight 0) — tests: DKIM selector records. Why: DKIM keys live at a selector name chosen by the sender. Probing common selectors would produce false negatives presented as findings, so it is left untested rather than guessed. - `dns.dmarc` **DMARC policy** (weight 1.2) — tests: _dmarc TXT record. Why: DMARC is what actually tells receivers to act on SPF and DKIM failures, and what gives you visibility into abuse of your domain. Without it, SPF alone changes little. - `dns.dnssec` **DNSSEC** (weight 0) — tests: DNSSEC validation. Why: Confirming DNSSEC requires a resolver that reports the authenticated-data flag. This host does not expose it, so the answer is unknown rather than negative. - `dns.mx` **Mail exchangers** (weight 0) — tests: MX records. Why: This domain does not receive mail. That is normal for a site-only domain, but it means the mail-authentication checks below apply differently. - `dns.ns` **Nameservers** (weight 0) — tests: NS records at this name. Why: Nameservers are published at the zone apex and inherited by subdomains. Their absence here is correct delegation, not a fault. - `dns.spf` **SPF record** (weight 1.3) — tests: v=spf1 TXT record. Why: Without SPF, anyone can send mail claiming to be from this domain and receiving servers have no way to reject it. This applies even to domains that never send mail. - `dns.txt` **TXT records** (weight 0) — tests: All TXT records at the apex. Why: Verification tokens accumulate; stale ones from decommissioned services are worth removing. ### TLS & Certificate (`tls`, weight 1.1, 10 checks) Affects: Browser trust, Availability, Data integrity, SEO. The certificate is the difference between a working site and a full-page browser warning. Expiry is the single most common cause of sudden, total, self-inflicted downtime on the web. If ignored: When the certificate lapses the site is effectively offline: every visitor meets an interstitial, and most leave. - `tls.available` **TLS availability** (weight 2.5) — tests: TLS handshake on port 443. Why: The server negotiates TLS successfully. - `tls.chain` **Certificate chain** (weight 1) — tests: Intermediate certificates presented. Why: Clients can build a complete path to a trusted root. - `tls.cipher` **Cipher strength** (weight 0.8) — tests: Negotiated cipher suite. Why: The negotiated suite is of current strength. - `tls.expiry` **Certificate expiry** (weight 2.5) — tests: notAfter date. Why: The certificate is comfortably current. - `tls.hostname` **Hostname match** (weight 1.8) — tests: Subject alternative names against the requested host. Why: The certificate was issued for this hostname. - `tls.issuer` **Certificate authority** (weight 0) — tests: Issuer of the presented certificate. Why: Who vouched for this certificate. - `tls.lifetime` **Certificate lifetime** (weight 0.6) — tests: Validity window of the certificate. Why: Within the limit browsers accept. - `tls.protocol` **TLS version** (weight 1) — tests: Negotiated protocol. Why: TLS 1.3 removes the legacy handshake weaknesses and completes in fewer round trips. - `tls.transparency` **Certificate transparency** (weight 0) — tests: Presence in public CT logs. Why: Confirming CT inclusion means asking a third-party log. This engine makes no external calls, so this is out of scope rather than failed. - `tls.trusted` **Certificate trust** (weight 2) — tests: Chain verification against the system trust store. Why: Browsers will accept the certificate without an interstitial warning. ### Security (`security`, weight 1.3, 18 checks) Affects: User safety, Data integrity, Browser trust, Liability. Security controls decide whether traffic can be intercepted, whether injected script can execute, and whether credentials or internal files are exposed to anyone who looks. If ignored: Ranges from browser warnings that scare users off, to account compromise and disclosure of data you are legally responsible for. Method note: Findings are inferred from HTTP responses. No certificate chain parsing or vulnerability database is used. - `sec.banner` **Version disclosure** (weight 0.8) — tests: Server and X-Powered-By headers. Why: No exact version is advertised. - `sec.blank` **target=_blank hygiene** (weight 0.7) — tests: rel on links opening a new tab. Why: New-tab links cannot reach back into this page. - `sec.cookies` **Cookie flags** (weight 0) — tests: Secure / HttpOnly / SameSite on Set-Cookie. Why: Nothing to evaluate. - `sec.cors` **CORS policy** (weight 1) — tests: Access-Control-Allow-Origin. Why: No cross-origin reads are permitted, which is the safe default. - `sec.crossorigin` **Cross-origin isolation** (weight 0.6) — tests: COOP / COEP / CORP headers. Why: These headers isolate your browsing context from cross-origin attacks like Spectre and tab-nabbing. - `sec.csp` **Content Security Policy** (weight 1.6) — tests: Content-Security-Policy header. Why: CSP is the main defence against cross-site scripting and injected content. - `sec.exposed` **Exposed paths** (weight 1.8) — tests: Probe of commonly leaked files. Why: Common sensitive paths are not reachable. - `sec.forms` **Form safety** (weight 0) — tests: Form action targets. Why: Nothing to evaluate. - `sec.framing` **Clickjacking protection** (weight 1.1) — tests: X-Frame-Options or CSP frame-ancestors. Why: Framing by third-party sites is restricted. - `sec.hsts` **HSTS** (weight 1.4) — tests: Strict-Transport-Security header. Why: Subdomains remain downgradeable. - `sec.https` **HTTPS** (weight 2.5) — tests: Scheme of the final URL. Why: Traffic is encrypted in transit. - `sec.inline` **Unsafe inline patterns** (weight 0.8) — tests: Dangerous constructs in inline script and attributes. Why: No obviously injectable inline patterns. - `sec.mixed` **Mixed content** (weight 1.3) — tests: http:// subresources on an https:// page. Why: Every subresource loads securely. - `sec.permissions-policy` **Permissions Policy** (weight 0.7) — tests: permissions-policy header. Why: Declares which browser features (camera, geolocation, microphone) the page may use. - `sec.redirect` **HTTP redirect** (weight 1.5) — tests: Behaviour of http:// requests. Why: Visitors arriving over HTTP are upgraded automatically. - `sec.referrer-policy` **Referrer Policy** (weight 0.9) — tests: referrer-policy header. Why: Controls how much of your URL leaks to third parties in the Referer header. - `sec.secrets` **Secrets in source** (weight 1.8) — tests: Credential patterns in the served HTML. Why: No recognisable credential patterns in the markup. - `sec.x-content-type-options` **X Content Type Options** (weight 1) — tests: x-content-type-options header. Why: Prevents browsers from second-guessing declared content types (MIME sniffing). ### Privacy & Policies (`privacy`, weight 0.9, 7 checks) Affects: Legal compliance, User trust, Regulatory risk. Consent and policy handling is the difference between lawful data collection and an enforceable violation. GDPR, ePrivacy and CCPA all attach real penalties. If ignored: Fines assessed on global turnover, plus the reputational cost of being seen to track people without asking. - `privacy.consent` **Consent before tracking** (weight 1.8) — tests: Tracking scripts versus a consent platform. Why: Nothing on the page triggers a consent obligation. - `privacy.cookies` **Cookies before consent** (weight 1.2) — tests: Set-Cookie on the first response. Why: Nothing is stored before the visitor has chosen. - `privacy.origins` **Third-party origins** (weight 0.8) — tests: Distinct external hosts loading resources. Why: A contained third-party surface. - `privacy.privacy` **Privacy policy** (weight 1.6) — tests: A linked Privacy policy page. Why: A privacy policy is legally required almost everywhere once you process any personal data. - `privacy.readability` **Policy readability** (weight 0.6) — tests: Flesch Reading Ease of the privacy policy. Why: GDPR requires privacy information in clear, plain language. A very difficult score suggests it is not. - `privacy.terms` **Terms of service** (weight 0.9) — tests: A linked Terms of service page. Why: Terms define the contract between you and your users. - `privacy.trackers` **Third-party tracking** (weight 1) — tests: Known analytics and advertising scripts. Why: No third-party tracking means no consent obligation from tracking. ### Infrastructure (`infra`, weight 0.9, 11 checks) Affects: Page speed, Core Web Vitals, Hosting cost, Reliability. Delivery decides how long a visitor waits before anything appears. Latency and caching behaviour feed directly into Core Web Vitals, which are a ranking input. If ignored: Measurable drop-off: bounce rate climbs with every additional second before first paint. Method note: Everything here is inferred from HTTP responses. No DNS lookup, WHOIS or certificate parsing is performed. - `infra.caching` **Caching headers** (weight 0.9) — tests: Cache-Control, ETag and Last-Modified. Why: Caching behaviour is explicitly declared. - `infra.cdn` **Delivery fingerprint** (weight 0) — tests: Server and CDN headers. Why: Identified from response headers alone. - `infra.compression` **Compression** (weight 1) — tests: Content-Encoding on the HTML response. Why: Compressed HTML transfers in a fraction of the bytes. - `infra.ip` **Resolved address** (weight 0) — tests: Peer address of the connection. Why: The address the connection actually terminated on. - `infra.protocol` **HTTP protocol** (weight 0.9) — tests: Negotiated protocol version. Why: Multiplexing removes the head-of-line blocking that limits HTTP/1.1. - `infra.redirects` **Redirect chain** (weight 1) — tests: Hops between the requested and final URL. Why: A single redirect is normal and inexpensive. - `infra.robots` **robots.txt** (weight 1) — tests: Reachability of /robots.txt. Why: Crawlers read this first; a missing or broken file wastes crawl budget. - `infra.sitemap` **XML sitemap** (weight 1.2) — tests: Reachability and parsing of sitemaps. Why: Sitemaps are the most direct way to tell a crawler what exists. - `infra.status` **Response status** (weight 1.5) — tests: HTTP status of the final URL. Why: The page is served successfully. - `infra.ttfb` **Time to first byte** (weight 1.1) — tests: Server response latency. Why: The server responds promptly. - `infra.weight` **HTML document size** (weight 0.8) — tests: Bytes of the HTML response. Why: Large HTML delays the parser before any asset is even discovered. ### Tech Stack & APIs (`tech`, weight 0.8, 9 checks) Affects: Page speed, Supply-chain risk, Maintainability. Every third-party script is code you do not control, executing with full access to your page. The stack determines both performance cost and how much trust you have extended to others. If ignored: A compromised vendor becomes a compromise of your site, and unbounded third-party weight quietly degrades performance. - `tech.endpoints` **API endpoints** (weight 0) — tests: URLs referenced by fetch/XHR/GraphQL in inline script. Why: Endpoints inside bundled external files are not visible without executing them. - `tech.iframes` **Embedded frames** (weight 0.6) — tests: iframe elements and their sandboxing. Why: No third-party document is embedded in the page. - `tech.inlinesize` **Inline script volume** (weight 0.7) — tests: Bytes of inline JavaScript. Why: Inline script cannot be cached separately and inflates every HTML response. - `tech.loading` **Script loading** (weight 1) — tests: async / defer on <script src>. Why: Script loading does not block the parser. - `tech.scripts` **Script inventory** (weight 0) — tests: Every <script src> on the page. Why: Each third-party script is code you do not control running on your origin. - `tech.sri` **Subresource Integrity** (weight 0.9) — tests: integrity attribute on third-party scripts. Why: Without SRI, whoever controls that host controls code running on your page. - `tech.stack` **Detected stack** (weight 0) — tests: Markup, asset paths and header fingerprints. Why: Identified from patterns in the served response, not from any external service. - `tech.styles` **Stylesheet inventory** (weight 0) — tests: Every external stylesheet. Why: External stylesheets are render-blocking by default. - `tech.thirdparty` **Third-party weight** (weight 1) — tests: Count of externally hosted scripts. Why: Each one is a blocking dependency, a privacy exposure and a supply-chain risk. ### Content Quality (`content`, weight 1, 10 checks) Affects: Engagement, Dwell time, Conversion, Perceived expertise. Content quality decides whether someone who arrives actually stays and acts. Readability and structure are what make the difference between skimming and reading. If ignored: Traffic arrives and leaves without converting, and the weak engagement signal feeds back into lower rankings. - `content.boilerplate` **Content vs chrome** (weight 0.7) — tests: Main content share of all page text. Why: The page is mostly content rather than navigation. - `content.filler` **Filler words** (weight 0.7) — tests: Intensifiers and hedges per 1000 words. Why: Prose is not padded with intensifiers. - `content.freshness` **Freshness signals** (weight 0.9) — tests: Publication and modification dates. Why: Undated content cannot be assessed for currency by readers or ranking systems. - `content.media` **Media richness** (weight 0.7) — tests: Images, video, lists and tables per volume of text. Why: The content is visually broken up. - `content.paragraphs` **Paragraph structure** (weight 0.8) — tests: Words per paragraph. Why: Paragraphs are a comfortable length. - `content.passive` **Passive voice** (weight 0.8) — tests: Share of sentences in passive construction. Why: Heavy passive voice hides who does what and reads as institutional. - `content.readability` **Readability** (weight 1.2) — tests: Flesch Reading Ease. Why: Dense prose loses general readers and reads as jargon to summarisers. - `content.sentences` **Sentence variety** (weight 1) — tests: Sentence length distribution. Why: Long average sentence length is the single biggest driver of unreadability. - `content.vocabulary` **Vocabulary richness** (weight 0.9) — tests: Type-token ratio over the first 1200 words. Why: Word choice is varied. - `content.volume` **Content volume** (weight 1.5) — tests: Words in the main content region. Why: There is enough writing to evaluate. ### AI-Slop Detector (`slop`, weight 0.8, 8 checks) Affects: Credibility, Ranking risk, Brand perception. Search systems increasingly discount unhelpful mass-produced content, and readers recognise it immediately. This is a measure of how generic the writing reads. If ignored: Content that costs money to produce, ranks poorly, and actively signals low quality to readers. Method note: A pattern-matching heuristic, not a classifier. Every point is traceable to the evidence listed on each check. - `slop.entities` **Concrete detail** (weight 1.8) — tests: Named entities, numbers and quotes per 1000 words. Why: The content is grounded in specifics. - `slop.headings` **Templated headings** (weight 1) — tests: Headings matching stock outline patterns. Why: The outline is not stock. - `slop.hedging` **Hedging and superlatives** (weight 1) — tests: Density of hedge words and superlatives. Why: Register is measured. - `slop.lists` **List-heavy structure** (weight 0.8) — tests: Ratio of list items to paragraphs. Why: Structure is not dominated by bullets. - `slop.overall` **Slop likelihood** (weight 2.5) — tests: Weighted combination of all signals above. Why: The text does not show the characteristic markers of generated filler. - `slop.paragraphs` **Paragraph uniformity** (weight 1) — tests: Variation in paragraph length. Why: Generated text often produces paragraphs of almost equal length. - `slop.tells` **Formulaic phrasing** (weight 2) — tests: Frequency of known LLM filler phrases. Why: Little to no stock phrasing. - `slop.uniformity` **Sentence uniformity** (weight 1.5) — tests: Coefficient of variation of sentence length. Why: Sentence rhythm varies naturally. ### Design & UX (`design`, weight 0.9, 10 checks) Affects: Mobile usability, Accessibility, Core Web Vitals, Conversion. Layout stability and responsive behaviour decide whether the page is usable on a phone, which is where most traffic is and which is what Google indexes. If ignored: Mobile visitors mis-tap, images jump as they load, and the page is penalised for it. Method note: Layout is assessed from markup and inline CSS only. No browser rendering is performed, so computed styles are not available. - `design.blocking` **Render-blocking assets** (weight 1) — tests: Stylesheets and synchronous scripts in <head>. Why: Few assets block the first paint. - `design.density` **DOM size** (weight 0.7) — tests: Element count and nesting depth. Why: Large, deeply nested DOMs slow style calculation and layout on every interaction. - `design.dimensions` **Image dimensions** (weight 1.1) — tests: width and height attributes. Why: Images without intrinsic dimensions cause layout shift as they load, which is measured by Cumulative Layout Shift. - `design.favicon` **Icon set** (weight 0.9) — tests: Declared favicons and touch icons. Why: The icon set covers tabs, bookmarks and home screens. - `design.fontsize` **Font size** (weight 0.7) — tests: Inline font-size declarations under 12px. Why: No obviously undersized inline text. - `design.lazy` **Lazy loading** (weight 0.8) — tests: loading attribute on images. Why: Offscreen images defer without delaying the hero. - `design.requests` **Subresource count** (weight 0.7) — tests: Scripts, styles, images and frames referenced. Why: A very high request count multiplies latency, especially on mobile networks. - `design.theme` **Theme and manifest** (weight 0.5) — tests: theme-color meta and web app manifest. Why: These control browser chrome colour and installability. - `design.tokens` **Design token count** (weight 0.6) — tests: Distinct colours and font stacks in the markup. Why: The visual vocabulary is contained. - `design.viewport` **Responsive viewport** (weight 1.8) — tests: meta viewport declaration. Why: The page adapts to device width and permits zoom. ### Accessibility (`a11y`, weight 1, 16 checks) Affects: Disabled users, Legal exposure, SEO, Usability for everyone. Accessibility decides whether people using a screen reader, keyboard or magnifier can use the site at all. Most of what fails here also degrades the experience for everyone else — unlabelled controls and broken heading order confuse sighted users too. If ignored: A share of your audience simply cannot complete the task. In the EU, UK and US this is also enforceable: accessibility lawsuits against websites run into the thousands each year. Method note: Automated checks find violations; they cannot prove conformance. Only about a third of WCAG criteria are machine-testable, and a clean result here is a starting point for manual testing, not a compliance statement. - `a11y.aria` **ARIA usage** (weight 1) — tests: Role validity and reference integrity. Why: Broken ARIA is worse than none: an invalid role or a reference to a missing id silently overrides the element's real semantics. WCAG 4.1.2. - `a11y.autoplay` **Autoplaying media** (weight 0.9) — tests: autoplay on audio and video. Why: Nothing plays audio without consent. - `a11y.captions` **Video captions** (weight 0.8) — tests: track elements on video. Why: Without captions the content is unavailable to deaf and hard-of-hearing users, and to anyone in a noisy place. WCAG 1.2.2. Note this only detects a declared track — it cannot judge caption quality, and embedded players are not visible to this check. - `a11y.contrast` **Colour contrast** (weight 0) — tests: Foreground and background colour pairs. Why: Real contrast depends on computed styles from stylesheets, inheritance and layering — which needs a rendering engine. Only inline colour pairs can be read from markup, and none were present. Reporting a pass here would be misleading. - `a11y.dupeids` **Duplicate IDs** (weight 1) — tests: Uniqueness of id attributes. Why: Label association, aria-labelledby and aria-describedby all resolve by id and take the first match. Duplicates silently point assistive technology at the wrong element. WCAG 4.1.1. - `a11y.headings` **Heading structure** (weight 1.2) — tests: Heading levels and order. Why: A broken outline makes heading navigation unreliable — users skip past content or lose their place in the hierarchy. - `a11y.iframetitle` **Frame titles** (weight 0.8) — tests: title attribute on iframes. Why: An untitled frame is announced only as "frame", giving no clue whether it is a video, a map or an advert. WCAG 4.1.2. - `a11y.imgalt` **Image alternatives** (weight 1.5) — tests: alt attribute on images. Why: An image with no alt is announced as "image" or by its filename, which tells a blind user nothing. Decorative images need alt="" so they are skipped. WCAG 1.1.1. - `a11y.labels` **Form labels** (weight 1.5) — tests: Accessible name on form controls. Why: Every input announces what it is for. - `a11y.landmarks` **Landmark regions** (weight 1.2) — tests: main, nav, header and footer regions. Why: Users of assistive technology can skip directly to the content. - `a11y.lang` **Document language** (weight 1.4) — tests: lang attribute on <html>. Why: Assistive technology can select the right pronunciation rules. - `a11y.name` **Interactive element names** (weight 1.4) — tests: Accessible name on links and buttons. Why: Every control announces its purpose. - `a11y.skiplink` **Skip link** (weight 0.7) — tests: A link that bypasses repeated navigation. Why: Keyboard users can jump straight to the content. WCAG 2.4.1. - `a11y.tabindex` **Tab order** (weight 0.8) — tests: Positive tabindex values. Why: Focus follows document order. - `a11y.title` **Page title** (weight 1.2) — tests: Non-empty <title>. Why: The page identifies itself when announced. - `a11y.zoom` **Pinch zoom** (weight 1.3) — tests: viewport scaling restrictions. Why: Users can magnify the page. ### Credibility & Honesty (`credibility`, weight 0.9, 8 checks) Affects: Trust, Conversion, Regulatory exposure. Attribution, evidence and honest framing decide whether a visitor believes the page. Dark patterns and unverifiable claims are now actively enforced against. If ignored: Visitors who do not trust the site do not buy from it, and manufactured urgency is a regulatory target. - `cred.author` **Author attribution** (weight 1) — tests: meta author, schema author and visible byline. Why: Unattributed content cannot demonstrate expertise. - `cred.citations` **Outbound citations** (weight 1) — tests: Links to external sources. Why: The content points to verifiable sources. - `cred.dates` **Publication date** (weight 0.9) — tests: Machine-readable dates on the page. Why: Undated content gives no way to assess currency. - `cred.guarantee` **Guarantee backing** (weight 0) — tests: Refund or guarantee claims versus a policy page. - `cred.identity` **Who is behind this** (weight 1.5) — tests: Links to about, contact or imprint pages. Why: Partial identity information leaves visitors unsure who they are dealing with. - `cred.pressure` **Pressure tactics** (weight 1.2) — tests: Urgency and scarcity language. Why: No manufactured urgency. - `cred.seals` **Trust seals** (weight 0.8) — tests: Security or certification badges and their verification links. Why: A seal that cannot be verified is decoration, and knowingly displaying a fake one is actionable. - `cred.socialproof` **Social proof backing** (weight 1) — tests: Claims of recognition versus verifiable evidence. Why: Claims are at least partially substantiated. ### Title-Content Match (`correlation`, weight 0.8, 7 checks) Affects: Bounce rate, Click-through rate, Ranking stability. When the title promises something the body does not deliver, the click happens and the visit fails. That mismatch is measurable and self-correcting downward. If ignored: High impressions, high bounce, and a steady slide in rankings as the mismatch is learned. - `correlation.clickbait` **Clickbait markers** (weight 0.8) — tests: Patterns in the title. Why: The title makes a straightforward claim. - `correlation.descbody` **Description covered by body** (weight 1.2) — tests: Token overlap between description and body. Why: A description promising content the page does not contain reads as bait. - `correlation.headings` **Headings addressed** (weight 1) — tests: Heading terms present in body text. Why: Sections deliver on their headings. - `correlation.slug` **URL slug matches title** (weight 0) — tests: Token overlap between URL path and title. Why: Homepages have no slug to match. - `correlation.stuffing` **Title keyword repetition** (weight 0.7) — tests: Repeated terms within the title. Why: Repeating a keyword inside the title is a recognised spam signal. - `correlation.titlebody` **Title covered by body** (weight 1.8) — tests: Token overlap between title and body. Why: Part of the title promise is not reflected in the content. - `correlation.titleh1` **Title matches H1** (weight 1) — tests: Token overlap between title and H1. Why: A title and H1 that disagree send conflicting topic signals. ### FAQ & Questions (`faq`, weight 0.7, 7 checks) Affects: Featured snippets, AI answers, Long-tail search. Question-and-answer structure is the format that both featured snippets and AI answers extract from. Answering directly beneath the question is what makes it extractable. If ignored: Content that answers the question loses the snippet to a competitor who structured it better. - `faq.answered` **Answers present** (weight 1.2) — tests: Text volume immediately after each question heading. Why: A question heading with no answer under it frustrates readers and produces empty retrieval chunks. - `faq.answerlength` **Answer depth** (weight 0.7) — tests: Average words per answer. Why: Very short answers rarely satisfy the query or survive as a citation. - `faq.headings` **Question-form headings** (weight 1) — tests: Headings phrased as questions. Why: Questions in headings align the page with real queries. - `faq.mismatch` **Schema matches page** (weight 0) — tests: Schema questions present in visible text. - `faq.schema` **FAQ schema** (weight 1) — tests: FAQPage or QAPage JSON-LD. Why: The questions are there; the machine-readable form is missing. - `faq.section` **FAQ section** (weight 0) — tests: An identifiable FAQ region. Why: Not every page needs an FAQ section. - `faq.titlequestion` **Title question answered** (weight 0) — tests: Title/description phrased as a question. ### Discoverability (`discoverability`, weight 1, 11 checks) Affects: Indexation, Crawl budget, New-page discovery. Robots rules, sitemaps and internal linking decide which pages are found and how often they are revisited. A page that is never crawled cannot rank regardless of quality. If ignored: New content takes weeks to appear, and orphaned pages never appear at all. - `disc.breadcrumbs` **Breadcrumbs** (weight 0) — tests: BreadcrumbList schema and breadcrumb markup. Why: Most useful on deep hierarchical sites. - `disc.broken` **Link health** (weight 1.2) — tests: HTTP status of sampled links. Why: Linking through redirects adds a hop for every visitor and crawler. - `disc.canonicals` **Canonical consistency** (weight 0.9) — tests: rel=canonical across sampled pages. Why: Inconsistent canonical coverage lets duplicate variants slip into the index. - `disc.coverage` **Sitemap coverage** (weight 1) — tests: Sitemap URLs versus pages linked from this page. Why: Pages you link to but do not declare are crawled later and less often. - `disc.depth` **Crawl depth** (weight 0.8) — tests: Directory depth of linked pages. Why: Linked pages sit close to the root. - `disc.feeds` **Content feeds** (weight 0) — tests: RSS or Atom feed declarations. Why: Only relevant for sites that publish a stream of content. - `disc.files` **Discovery files** (weight 0) — tests: Probe of conventional well-known files. Why: These files are optional but each one answers a question a machine would otherwise guess at. - `disc.lastmod` **Sitemap freshness** (weight 0.8) — tests: lastmod values. Why: lastmod is how crawlers decide what to re-fetch first. - `disc.robots` **robots.txt** (weight 1.1) — tests: Presence and parse of /robots.txt. Why: Crawler rules are explicit and parse cleanly. - `disc.sitemap` **XML sitemap** (weight 1.4) — tests: Sitemap discovery and parsing. Why: Errors mean some declared URLs never reach the crawler. - `disc.sitemapref` **Sitemap in robots.txt** (weight 0.8) — tests: Sitemap: directive. Why: Crawlers are pointed straight at the sitemap. ### Contact & Exposure (`contact`, weight 0.7, 8 checks) Affects: Trust, Lead capture, Spam exposure, Compliance. Reachability is a basic legitimacy signal and, in several jurisdictions, a legal requirement. How addresses are published also decides how much spam you receive. If ignored: Prospective customers with a question simply leave, and exposed addresses are harvested within days. - `contact.email` **Contact email** (weight 1) — tests: Any email address on the page. Why: A reachable address is a basic trust signal and a requirement in several jurisdictions. - `contact.form` **Contact form** (weight 0) — tests: Forms with contact-shaped fields. Why: A contact form is one way to offer contact without publishing an address. - `contact.inventory` **Email inventory** (weight 0) — tests: Addresses found in text, mailto links, JSON-LD and scripts. Why: Every address here is visible to anyone who reads the page source. - `contact.page` **Contact page** (weight 1) — tests: A discoverable contact or imprint page. Why: No route to contact the operator was found from this page. - `contact.personal` **Personal addresses** (weight 0.6) — tests: Non-role addresses in the markup. Why: Individual addresses attract targeted phishing and are personal data under GDPR. - `contact.phone` **Phone number** (weight 0.7) — tests: Phone patterns and tel: links. Why: For commercial sites a phone number materially raises trust. - `contact.scraping` **Scraper exposure** (weight 0.7) — tests: Plain-text addresses in the markup. Why: Unobfuscated addresses in page text are harvested by spam crawlers within days. - `contact.securitytxt` **security.txt** (weight 0.6) — tests: A published security contact. Why: security.txt is how researchers report vulnerabilities to you instead of elsewhere. ## Not checked — do not claim otherwise - WHOIS, domain age and registrar data - DNSSEC validation (needs a resolver reporting the AD flag) - DKIM (selector names are not discoverable from DNS) - Certificate transparency log inclusion (needs an external log service) - Known-vulnerability and CVE databases - PageSpeed, Lighthouse and field Core Web Vitals - Colour contrast from stylesheets (needs a rendering engine; only inline pairs are measurable) - Anything requiring JavaScript execution — the page is analysed as served - Backlinks, domain authority, traffic and ranking positions - HTTP/3 support (the HTTP client here lacks it) Accessibility results find violations but cannot prove conformance: only about a third of WCAG criteria are machine-testable. Infrastructure findings are inferred from response headers and labelled as inferred. Sites that block server-side requests are scored on what is reachable, with the limitation stated rather than a number invented.