How Health Scores Are Computed

Every domain you monitor gets a Health score from 0 to 100. Here is exactly what that number means and how it is calculated.

The big picture

Your overall score is a weighted average of four categories, each normally worth 25% of the total:

Category Default weight What it measures
Availability 25% How often your site was reachable over the last 30 days
SSL certificate 25% Certificate expiry, TLS version, HTTP→HTTPS redirect
Lighthouse 25% Performance, accessibility, SEO and best practices (0–100 each)
Security 25% Result of the latest security scan of your URLs

Missing data never penalizes you. Categories that have no data yet (for example, a fresh domain with no Lighthouse report or no security scan) are simply excluded from the average, and the remaining weights are re-normalized to 100%. A brand-new website therefore starts at a neutral score instead of being dragged down by "no data" rows.

Traffic light thresholds

Each category — and the overall score — is also shown as a traffic light:

Green Score ≥ 90
Yellow Score ≥ 70 (overall) / ≥ 50 (categories)
Red Score below the yellow threshold

Availability and SSL use their own specialized thresholds (below).

1. Availability (uptime)

Availability is the percentage of successful checks over the last 30 days:

uptime = successful checks / total checks × 100

Traffic light thresholds:

  • Green ≥ 99.9%
  • Yellow ≥ 99.0%
  • Red < 99.0%

Two honest caveats:

  • Uptime is only reported once your domain has at least 50 recorded checks (roughly an hour of monitoring at the default 1-minute cadence). Until then the card shows "No data" — otherwise a domain that simply failed its very first checks would show a misleading 0%.
  • The percentage covers the last 30 days. A domain tracked for only a few hours shows a "Tracked for X" note so a short window is not mistaken for a long-term rate. The availability score used in the composite is exactly this percentage.

2. SSL certificate

The SSL score starts from days until certificate expiry and is scaled as a fraction of 30 days:

score = min(days remaining ÷ 30, 1) × 100

Condition Status Score impact
Certificate expired Red Score 0
Fewer than 14 days left Red Proportional (e.g. 7 days → 23/100)
14 to 29 days left Yellow Proportional
≥ 30 days left Green 100

On top of the expiry-based score, penalties are subtracted:

  • −40 when the site still negotiates a deprecated TLS version (TLS 1.0 or TLS 1.1).
  • −30 when the site does not redirect HTTP to HTTPS.

The score is never negative — it is floored at 0. These rules are why a certificate that expires in a week scores far lower than one valid for 90 days: your headroom before a browser warning matters.

3. Lighthouse

The Lighthouse category is the weighted average of four sub-scores. Each sub-score is Lighthouse's own 0–100 output (a Beta/DevTools score):

Sub-category Weight (of 100)
Performance 40
Accessibility 30
SEO 20
Best practices 10

lighthouse score = (Perf×40 + Acc×30 + SEO×20 + BP×10) / 100

Sub-scores that are missing (no Lighthouse audit run yet) are skipped and the remaining weights are re-normalized. Traffic lights: green ≥ 90, yellow ≥ 50, otherwise red.

4. Security

The Security score is the average of each monitored URL's latest security scan score, so the score you see here matches the one on your Security page. The Score Breakdown additionally shows the check groups that make up those scans:

Group Weight (of 100)
Headers 30
Network 22
Cookies 15
HSTS Preload 12
Rate limiting 11
Technology 10

Traffic lights: green ≥ 90, yellow ≥ 50, otherwise red. Every individual check inside these groups is documented in Security checks in detail below.

Security checks in detail

Every security scan runs the check groups below against your URL. Each check either passes (recorded as a passed check) or produces a finding with a severity: critical −15, high −8, medium −4, low −1, info −0 points from the 100-point score.

Every test is documented the same way: How it works describes exactly what the scanner does on the wire, what is checked lists every rule with its severity cost, why it matters explains the attack or failure the test detects, and what to do points at the usual fix.

Network group (22% of the Security score)

TLS — HTTPS & certificate analysis

How it works — The scanner resolves the domain, validates the target against the SSRF guard (private and reserved addresses are rejected before any connection), then performs a genuine TLS handshake against port 443 with a 10-second timeout. It offers HTTP/2 and HTTP/1.1 via ALPN but deliberately skips certificate verification, so it can inspect a broken or expired chain instead of aborting. From the handshake state it reads the negotiated protocol version, the cipher suite and the full peer certificate chain. Finally, an independent HEAD request to the stored URL checks whether plain-HTTP traffic is redirected to HTTPS.

What is checked:

  • HTTPS availability — whether the stored URL uses HTTPS and the handshake succeeds. An HTTP-only site → high (−8); an HTTPS site passes.
  • TLS version — read from the handshake: TLS 1.3 and 1.2 pass; TLS 1.0/1.1 → high (−8); SSL 3.0 → critical (−15).
  • Cipher suite — RC4, 3DES and CBC-with-RSA suites → medium (−4).
  • Certificate — the presented chain is inspected end to end: no certificate → high; expired → critical (−15); expires within 30 days → medium; self-signed (issuer equals subject) → high; the domain not covered by the SANs, wildcards honored → high; RSA keys below 2048 bits → medium.
  • HTTP/2 — whether ALPN negotiated h2, otherwise a low finding. HTTP/3 is probed via the Alt-Svc header and is informational only.
  • HTTP→HTTPS redirect — a 3xx response with an https:// Location when the stored URL is HTTP records a pass.

Why it matters — TLS is the layer that turns the internet's plaintext pipes into private ones. Deprecated protocol versions and weak ciphers have known, scriptable breaks (POODLE against SSL 3.0, downgrade and padding attacks against old CBC suites), letting an on-path attacker read or alter traffic. An expired or mismatched certificate is worse for business than for cryptography: visitors meet a full-page browser warning, and most never click through. The HTTP→HTTPS redirect check closes the last gap where someone typing example.com lands on plaintext first.

How it could be exploited — An attacker on the same network (coffee-shop Wi-Fi, a compromised router) intercepts the visitor's first HTTP request and answers it themselves, proxying to your real site — every byte, including passwords, flows through them in plaintext (SSL-stripping). With an expired or self-signed certificate they count on the visitor clicking through the browser warning once; after that they impersonate the site for the whole session. Weak protocol versions let recorded sessions be decrypted offline.

What to do — Serve TLS 1.2+ only (ideally 1.3), automate certificate renewal (certbot or managed CDN TLS), and add a single 301 redirect from HTTP to HTTPS. Each finding in the product carries copy-paste fix snippets for nginx, Apache and Cloudflare.

DNS — records, email policy, DNSSEC, subdomains

How it works — The test runs a sequence of read-only DNS queries: first the five core record types (A, AAAA, MX, NS, CNAME) through the system resolver, then TXT lookups for the SPF policy and the _dmarc subdomain. CAA and DNSSEC are probed with hand-built DNS packets (record types 257 and 48) sent to a public resolver over UDP with a TCP fallback, 3-second timeouts. Finally the scanner resolves roughly 35 common subdomain names — www, mail, dev, staging, test, jenkins, grafana, kibana, vpn, phpmyadmin, and others — and classifies any that answer by how risky their purpose looks.

What is checked:

  • A record — must resolve publicly; a private/internal IP → low; no A record → high. Missing AAAA, MX or NS → info.
  • SPF — a TXT record starting with v=spf1 is parsed; missing → medium; a policy ending in +all or bare all → high.
  • DMARC — _dmarc.<domain> must carry v=DMARC1; missing → medium; policy p=none (monitor-only, enforces nothing) → low.
  • CAA — no Certification Authority Authorization record → info.
  • DNSSEC — no DNSKEY answer means the zone is unsigned → medium.
  • Subdomain discovery — discovered dev/staging/admin/monitoring-style hosts are enumerated in a medium finding; a clean sweep records a pass.

Why it matters — DNS is the trust root of the domain, and each missing piece is a different attack. Without SPF and DMARC anyone can send email that appears to come from your domain — the raw material of invoice fraud and credential phishing against your customers and staff. Without DNSSEC, a poisoned cache can silently send your visitors to an attacker's server. Without CAA, any certificate authority on the web may issue a certificate for your domain, widening the mis-issuance risk. And forgotten subdomains — the test server someone stood up in 2022 and never patched — are one of the most common real-world breach entry points.

How it could be exploited — With no SPF/DMARC the attacker emails your customers 'from' your domain — invoice fraud and phishing land in inboxes carrying your name. A subdomain sweep finds the forgotten staging box, which typically runs unpatched software with default credentials; from there the attacker pivots inward. Unsigned DNS lets a poisoned resolver silently point your domain at the attacker's server, and without CAA a fraudulent certificate can be issued at any CA, enabling a perfectly valid-looking impersonation.

What to do — Publish an SPF policy with a hard ending (-all or ~all), roll DMARC out from p=none to p=quarantine/p=reject once reporting looks clean, enable DNSSEC at your registrar, restrict certificate issuance with a CAA record, and take inventory of everything that answers under your domain.

Redirects — chain analysis

How it works — The scanner walks your redirect chain exactly as a browser would: it sends a HEAD request to the stored URL, reads the Location header, resolves relative redirect targets against the current URL, and repeats — up to 10 hops. Every visited URL is remembered so a repeated URL is caught as a loop, and the scheme plus status code of every hop is recorded. The walk stops at the first non-3xx response, a missing Location header, or a loop.

What is checked:

  • Chain length — more than 5 hops → low; a short, reachable chain passes.
  • Redirect loop — a URL visited twice → medium.
  • HTTPS→HTTP downgrade — any hop from an https:// URL to an http:// URL → high.
  • External domain — any hop leaving your domain → medium.

Why it matters — A downgrade hop is the dangerous one: part of the journey happens in plaintext, where the connection can be read or rewritten. A hop to a different domain can mean a hijacked plugin, a misconfigured CDN, or SEO spam someone injected — all worth catching early. Loops mean the page is effectively down for every visitor. And long chains are not just latency: every extra hop is another DNS lookup, TLS handshake and round-trip before the user sees anything.

How it could be exploited — The downgrade hop is the goldmine: an on-path attacker intercepts the plaintext hop and injects or rewrites the redirect target, sending the victim to a lookalike site that harvests credentials. A redirect to a foreign domain is used for phishing: the link starts with your real domain, and the victim only ever sees that. A redirect loop is its own denial of service.

re the connection can be read or rewritten. A hop to a different domain can mean a hijacked plugin, a misconfigured CDN, or SEO spam someone injected — all worth catching early. Loops mean the page is effectively down for every visitor. And long chains are not just latency: every extra hop is another DNS lookup, TLS handshake and round-trip before the user sees anything.

What to do — Redirect once, directly to the final HTTPS URL. Never chain through HTTP, and audit any redirect that leaves your own domain.

HTTP Methods — TRACE & OPTIONS

How it works — Two live requests are sent to your URL. First a TRACE request: a server that answers HTTP 200 (echoing the request back) has the method enabled. Then an OPTIONS request whose Allow response header is parsed into a method list; anything in the TRACE/PUT/DELETE/PATCH family is flagged as advertised. If both probes come back empty, the test records a clean pass.

What is checked:

  • TRACE — HTTP 200 to a TRACE request → medium.
  • OPTIONS — TRACE/PUT/DELETE/PATCH in the Allow header → low; only safe methods → pass.
  • Neither probe applicable → pass (“No dangerous methods detected”).

Why it matters — TRACE enables Cross-Site Tracing: in vulnerable setups the echo response leaks headers — including cookies — to script running on another site, defeating even the HttpOnly flag. An OPTIONS advertisement of PUT or DELETE tells an attacker which state-changing verbs the server will consider; on endpoints that trust the verb rather than authenticating properly, that is content upload or deletion.

How it could be exploited — The attacker lures the victim to their page, which makes the victim's browser send a TRACE request to your server and reads the echoed response — headers, cookies, internal URLs. On servers that honor PUT, the attacker uploads a webshell directly. Even where methods are merely advertised, the OPTIONS response is reconnaissance handed over for free.

What to do — Disable TRACE on the web server (TraceEnable off on Apache, a method guard on nginx) and restrict allowed methods to what the application actually uses.

HTTP Probes — server exposure

How it works — The test sends real requests to a catalogue of well-known exposure paths and fingerprints the answers rather than trusting status codes alone: a directory listing is recognized by its markup, a debug screen by framework-specific stack-trace signatures (Django, Laravel, Flask, ASP.NET and friends), a source map by its JSON content, and GraphQL introspection by posting a live introspection query and checking whether the schema comes back. Each class has its own path list — admin panels (/admin, /wp-admin, /administrator…), server status and phpinfo-style endpoints, backup artifacts, JavaScript .map bundles and common GraphQL endpoints.

What is checked:

  • Directory listing — indexable paths answering with a listing → medium.
  • Admin panels — reachable unauthenticated → high.
  • Backup & sensitive files — .bak, .sql, .env-style artifacts answering → high.
  • Information disclosure — /server-status and phpinfo-style pages → findings.
  • Debug mode — framework error screens with stack traces → medium.
  • Source maps — .map files shipped alongside your JavaScript → low.
  • GraphQL introspection — a live schema query succeeding → medium.

Why it matters — Each of these hands an attacker a different key to the same lock. A directory listing draws the map of your file tree. A public admin panel turns into a brute-force target the moment it is found. Backup files and .env artifacts regularly contain database credentials. Source maps ship your unminified source — including comments — to anyone who asks. An open GraphQL introspection endpoint hands over your entire API schema, documenting every query an attacker can try. Automated attack bots scan for exactly these paths continuously; leaving one open is a matter of when, not if.

How it could be exploited — The chain: the directory listing reveals a backup archive, the archive contains database credentials, the credentials open the production database — no exploit against your code was ever needed. Debug screens leak connection strings; GraphQL introspection documents the hidden admin fields the attacker then queries directly. Public admin panels enter the botnet wordlists the same day they are indexed.

What to do — Disable directory listing, require authentication on admin routes, remove backup artifacts from the webroot, turn source maps off in production builds, and disable GraphQL introspection in production.

Port Scan — exposed services

How it works — The scanner attempts real TCP connections to a fixed list of ports that commonly host sensitive services — databases (MySQL 3306, PostgreSQL 5432, MongoDB 27017), caches (Redis 6379, Memcached 11211), mail (25, 587), remote administration (SSH 22, RDP 3389), FTP (21) and others — each with a short connection timeout so the sweep stays quick. A completed handshake means the port is open and the service is reachable from the internet. Open sensitive ports are reported as findings with the service identified; only low-risk services, or none at all, record passes.

Why it matters — A database or cache that answers the public internet is one of the most common breach patterns there is: Redis and MongoDB instances without authentication have been wiped for ransom at scale, exposed RDP is the classic ransomware entry point, and an open SMTP relay turns your server into a spam source. These services usually have no business being publicly reachable at all.

How it could be exploited — Exposed Redis or MongoDB without authentication is instant compromise — attackers connect, exfiltrate or wipe the data, and often leave a ransom note. Exposed RDP gets brute-forced and is the classic ransomware entry point; an open SMTP relay turns your IP into a spam source and gets you blocklisted, taking legitimate mail down with it.

What to do — Bind services to localhost or a private network and put a firewall in front of everything else. If a service must be remote, gate it behind a VPN or an allow-list.

Email Security — SPF, DKIM, MTA-STS, TLS-RPT, BIMI

How it works — This test grades the domain's mail-authentication stack record by record, all through DNS (plus one HTTPS fetch): DKIM selector lookups check for signing keys and flag plaintext signing modes; MTA-STS is validated in two places — the _mta-sts DNS record and the actual policy file served at https://mta-sts.<domain>/.well-known/mta-sts.txt, whose mode field is graded (enforce passes, none does not); the _smtp._tls TLS-RPT record is checked for a usable reporting URI; and the BIMI record is looked up purely informationally.

What is checked:

  • DKIM — no signing record → high; plaintext signing (k=plain) → medium.
  • MTA-STS — missing record/policy → medium; malformed policy → finding; mode none → finding; enforce → pass.
  • SMTP TLS-RPT — missing record → finding; no reporting URI → finding; configured → pass.
  • BIMI — informational; a Verified Mark Certificate records a pass.

Why it matters — SPF and DMARC (covered by the DNS test) say who may send for you; DKIM cryptographically signs each message so receivers can verify it was not altered. Together they are the difference between your invoices reaching inboxes and someone sending phishing email in your name. MTA-STS and TLS-RPT then protect the transport itself: they force mail servers to use TLS when talking to you and give you visibility when they fail — closing the downgrade hole where mail quietly falls back to plaintext SMTP.

How it could be exploited — The attacker sends an urgent invoice 'from' your CFO and it lands in the inbox, because nothing tells the receiver your domain doesn't send mail from that server. Without MTA-STS the receiving mail server downgrades the connection to plaintext SMTP and the message is read in transit; without TLS-RPT nobody ever notices.

What to do — Publish DKIM keys for your sending services, deploy an MTA-STS policy in testing mode and move to enforce, and add a TLS-RPT record pointing at your reporting address.

Headers group (30% of the Security score)

Headers — security response headers

How it works — The page is fetched with a GET request (15s timeout, up to 5 redirects followed) and the response headers are evaluated one by one against current hardening recommendations. A Content-Security-Policy is additionally parsed directive by directive — split on semicolons into script-src, style-src, object-src, base-uri, form-action and friends — and each source list is inspected for unsafe-inline, unsafe-eval and wildcard entries. Sensitive paths (login, register, account, admin in the URL) get an extra Cache-Control evaluation.

What is checked:

  • HSTS — present → pass; missing → medium (−4).
  • CSP — present → pass, then parsed: script-src with unsafe-inline/unsafe-eval → high each; wildcard * or missing script-src → medium; style-src unsafe-inline → low; object-src wildcard/missing → medium/low; base-uri/form-action wildcard → medium; nonce/hash usage → pass.
  • X-Content-Type-Options: nosniff — missing/wrong → low.
  • X-Frame-Options — missing unless CSP frame-ancestors covers it → low.
  • Referrer-Policy / Permissions-Policy — missing → info each; COOP/CORP/COEP pass when present.
  • Information leaks — Server and X-Powered-By version strings → info each.
  • Cache-Control — on login/account/admin-style paths, missing no-store → low.

Why it matters — Security headers are the cheapest defense-in-depth available — most take one line of server or CDN configuration. CSP constrains what an injected script can do, X-Frame-Options blocks clickjacking, HSTS stops protocol downgrades, nosniff prevents MIME confusion, and Referrer/Permissions policies stop URL and capability leakage. Version-revealing Server/X-Powered-By headers hand attackers a target list for known CVEs.

How it could be exploited — One reflected injection point (search box, URL parameter, comment field) becomes full account takeover when script-src allows unsafe-inline: the victim's browser runs the attacker's JavaScript, which reads the page and calls your API with the victim's cookies — the victim sees nothing. Without X-Frame-Options the attacker stacks invisible buttons over your 'Confirm payment' button. Without HSTS the first request is downgraded to plaintext and rewritten.

What to do — Add the missing headers at your server or CDN edge (copy-paste configs ship with each finding), then tighten CSP incrementally: start with object-src 'none' and move inline scripts to nonces or hashes.

CORS — cross-origin policy

How it works — A GET request is sent with a deliberately attacker-chosen Origin: https://evil-attacker-example.com header — without an Origin most servers return no CORS headers at all. The response's Access-Control-* headers are then evaluated: wildcard origin combined with credentials, state-changing methods advertised to any origin, sensitive headers exposed, and preflight caching configuration.

What is checked:

  • Wildcard + credentials — Allow-Origin: * with Allow-Credentials: true → high.
  • No CORS headers → pass (browser default is restrictive); wildcard without credentials → pass (public API).
  • Dangerous methods with wildcard — DELETE/PUT/PATCH to any origin → medium.
  • Exposed sensitive headers — Authorization/X-Api-Key/X-Auth-Token/Set-Cookie in Expose-Headers → medium each.
  • Preflight not cached — missing Access-Control-Max-Age → low.

Why it matters — A misconfigured CORS policy lets any website you don't control read authenticated responses from your API in the visitor's browser — effectively defeating the same-origin policy and leaking user data cross-site. Wildcard-plus-credentials is the most dangerous combination.

How it could be exploited — The attacker hosts a page that fires a request against your API from the victim's browser — with wildcard-plus-credentials the response (balances, profile data, tokens) is readable cross-origin, so every user who visits the attacker's page while logged in to your app gets their data exfiltrated. No exploit against your code, no malware, one page view.

What to do — Replace wildcard Allow-Origin with an explicit allow-list of trusted domains, never combine * with credentials, and expose only headers clients genuinely need.

Security Policy — security.txt (RFC 9116)

How it works — /.well-known/security.txt and /security.txt are fetched and parsed for the required fields — Contact in particular — and graded against RFC 9116 (canonical URI, field syntax). A valid file records passes; an incomplete or malformed one a finding; absence of any security contact a finding.

What is checked: security.txt found and RFC-valid → pass; incomplete (e.g. missing Contact) → low; none published → info.

Why it matters — Without a published contact, someone who finds a vulnerability may sit on it, sell it, or dump it publicly. security.txt gives researchers a legitimate, discoverable channel — often the difference between a coordinated report and a public exploit.

How it could be exploited — Less an exploit than an amplifier: the researcher who would have reported for a bounty sells the finding instead, and attacker reconnaissance marks your domain as one that never receives reports — you are targeted precisely because you are silent.

What to do — Publish /.well-known/security.txt with Contact and Expires fields, and keep the address monitored.

Cookies group (15% of the Security score)

Cookies — cookie flags & scope

How it works — A real page load is made (GET, 15s timeout) and every Set-Cookie header is parsed. Each cookie is evaluated individually: Secure and HttpOnly flags, SameSite attribute, and Domain scope (TLD-only values like .com are dangerously broad). Names containing session, sid, token, auth, jwt, csrf, phpsessid and similar are treated as session cookies and held to stricter HttpOnly/SameSite requirements. Duplicate cookie names collapse to their worst status.

What is checked:

  • Secure flag — missing on an HTTPS site → medium; set but the site is plain HTTP → low.
  • HttpOnly — missing on a session-like cookie → medium.
  • SameSite — missing on a session cookie → low.
  • Domain scope — TLD-only domain → high.
  • No cookies at all → pass; a cookie with no issues → per-cookie pass.

Why it matters — Without Secure the cookie travels over plain HTTP where anyone on the network can read it. Without HttpOnly any XSS — even a third-party script — can read the session cookie and hijack the login. Without SameSite, cross-site requests silently carry the cookie, enabling CSRF. A TLD-wide Domain scope hands the cookie to every site under that TLD.

How it could be exploited — The attacker's XSS payload (or a compromised third-party script) reads the session cookie via document.cookie and sends it home in one request; the attacker replays it and is the user — no password needed. On open Wi-Fi without Secure, passive sniffing suffices. Without SameSite, a third-party page makes the victim's browser silently issue authenticated requests.

What to do — Set Secure, HttpOnly and SameSite=Lax (or Strict) on every session cookie and scope Domain as narrowly as possible.

Technology group (10% of the Security score)

HTML — page content risks

How it works — The full page source is downloaded (15s timeout) and parsed: every script, stylesheet, iframe, form action, image, inline style and CSS @import/url() reference is checked for an http:// URL on an HTTPS page (mixed content). Cross-origin scripts and stylesheets are checked for an integrity (SRI) attribute. Password forms submitting to a plaintext URL are flagged, and the page's base URL is probed for directory listing and backup-file exposure.

What is checked:

  • Mixed content — HTTP script/iframe/style/form/image on an HTTPS page → medium each; none → pass.
  • SRI — external scripts without integrity → medium; configured → pass.
  • Password form over HTTP → finding.
  • Directory listing & backup files probed from the page's base URL.

Why it matters — Active mixed content (scripts, iframes over HTTP) is blocked by browsers — breaking the page — and passive mixed content leaks traffic and undermines the padlock. Without SRI a compromised CDN can silently tamper with your JavaScript. A login form posting over HTTP hands credentials to anyone on the path.

How it could be exploited — An on-path attacker rewrites the plaintext script response to their own JavaScript: despite the HTTPS padlock in the URL bar, the attacker's code runs inside your page with full access to forms, inputs and session. A compromised CDN takes the same power global for every SRI-less script you load.

What to do — Load every subresource over HTTPS, add integrity attributes to third-party scripts, and serve login forms — and their action targets — over HTTPS only.

Sensitive Files — backup & config artifacts

How it works — A list of commonly exposed sensitive paths is probed with HEAD/GET requests: .env files, .git directories, SQL dumps, editor backups (.bak, ~), archive artifacts. Any response that is not 404/410 — including 401/403, which reveal the file exists — is recorded as an exposed artifact with its path.

What is checked: nothing reachable → pass; publicly accessible sensitive file → high.

Why it matters — These files routinely contain database credentials, API keys and full source or data dumps. Automated bots probe for exactly these names continuously; .env and .git exposure alone has compromised countless deployments.

How it could be exploited — The attacker downloads the exposed .env, extracts the database credentials, and logs in directly — no exploit chain needed, the door was open. If the file contains cloud keys or SMTP credentials, they pivot: spam through your SES, resources on your AWS bill, data out through your own infrastructure.

What to do — Remove backups and dumps from the webroot entirely, keep secrets out of the document root, and block dotfiles at the server.

Mixed Content — insecure subresources

How it works — The same page-source parse as the HTML module, filtered to insecure-scheme references: scripts and iframes count as active mixed content (the dangerous, browser-blocked kind); stylesheets, images, form actions and CSS references count as passive. Each found reference is reported with its element type.

What is checked: no active mixed content → pass; any http:// reference on an HTTPS page → medium.

Why it matters — Browsers block active mixed content outright (breaking the page) and upgrade-or-block passive content; even where it loads, the plaintext request is readable and modifiable on the path — a padlock with holes.

How it could be exploited — Same chain as the HTML case, focused on the dangerous subset: the plaintext script or iframe response is replaced in transit, and the attacker's code executes in your origin — the padlock in the address bar tells the victim it is safe while it happens.

What to do — Rewrite all subresource URLs to https:// (or protocol-relative), and fix hardcoded http:// references in CMS themes and templates.

Technology — stack fingerprinting

How it works — Response headers (Server, X-Powered-By) and HTML markers (generator meta tags, framework-specific markup patterns, known script/asset paths) are matched against fingerprints of known technologies. Identified versions with published issues produce findings; nothing detectable → pass (the stack is not leaking its identity).

What is checked: no fingerprint → pass; technology/version with known issues → medium.

Why it matters — Old framework versions have published CVEs with working exploits. Knowing the stack is the first step of both patching (for you) and targeting (for attackers) — the same fingerprinting runs on both sides.

How it could be exploited — Fingerprinting yields 'PHP 7.4 + framework X 2.3'; the attacker pulls the matching public exploit from any CVE database and runs it — the whole chain is automated in commodity attack tooling that sweeps the internet continuously.

What to do — Keep the stack current, and strip or genericize version-revealing headers and generator tags.

Privacy — tracking & browser permissions

How it works — The page is fetched and its source and storage scanned: known analytics/pixel script fingerprints are matched; localStorage is inspected for tracking entries; external resource domains are collected; a link/footprint scan looks for a privacy policy and consent platform; the Do Not Track acknowledgement is checked; and permission requests (camera, microphone, geolocation) are evaluated against the Permissions-Policy header.

What is checked:

  • Tracking scripts — without a consent mechanism → low; with consent → pass.
  • Privacy policy & cookie banner — missing → low each; present → pass.
  • Do Not Track — server acknowledgement (informational).
  • Permissions — sensitive requests → low; none → pass.
  • localStorage tracking / third-party resources → low / info.

Why it matters — Tracking without consent is a compliance risk (GDPR/CCPA) and a trust risk. Full URLs leaking via Referer and permissive feature policies let embedded third parties observe user paths and request sensitive browser capabilities. Each third-party script is also code you don't control executing on your page.

How it could be exploited — The exploit here is regulatory and reputational: a user who discovers they are still tracked after opting out (localStorage identifiers survive cookie clearing) can report you under GDPR — the fine is the exploit. Third-party scripts also give every one of those parties a foothold in your page that they can lose to attackers.

What to do — Gate tracking behind a real consent mechanism, link a privacy policy, restrict Permissions-Policy (camera/microphone/geolocation denied by default), and audit which third parties you load.

Auth — OAuth surface & credential leaks

How it works — Three probes run against the live page and well-known auth paths: the page source is scanned for hardcoded credentials (API keys, secrets, tokens matching common patterns); OAuth/OpenID discovery endpoints (.well-known/openid-configuration, .well-known/jwks and similar) are probed for open exposure including debug variants; and common login/redirect paths are tested with a foreign redirect_uri/return parameter to see whether the flow accepts leaving the trusted origin.

What is checked: credentials in page source → high when found; exposed OAuth/debug endpoints → medium; open redirect in the auth flow → high; clean on all three → passes.

Why it matters — Hardcoded keys in shipped HTML are live credentials anyone can copy. Open discovery plus permissive redirect handling is the exact recipe for authorization-code interception — an attacker crafts a login link that returns the user (and their code/token) to a domain they control.

How it could be exploited — A key in the page source is used within minutes of being committed — bots scan public pages and repos for key patterns continuously. An open redirect on /login lets the attacker craft a link to your real login page that bounces the victim — and their authorization code or token — to the attacker's endpoint, who is now logged in as the victim.

What to do — Never ship secrets in client code (move them server-side), restrict OAuth redirect URIs to an exact allow-list, and disable debug/discovery endpoints in production.

HSTS Preload (12% of the Security score)

HSTS Preload — browser preload-list eligibility

How it works — The Strict-Transport-Security header is validated against the preload requirements — max-age of at least one year, includeSubDomains and the preload directive present. Then the hstspreload.org API is queried live for the domain's current status.

What is checked: preloaded with includeSubDomains → pass; preload entry lacking includeSubDomains → medium; not preloaded → medium.

Why it matters — Preloading HSTS is the only defense that stops the very first request from being downgraded to HTTP — the hole SSL-stripping attacks live in (coffee-shop Wi-Fi, malicious routers). Header-only HSTS protects only from the second visit onward.

How it could be exploited — The attack is the downgrade itself: on hostile Wi-Fi the attacker answers the first HTTP request before any HSTS policy could have been learned — only preload closes that window. Until then, every first-ever visit on every new device is spoofable, which is exactly the moment users type their password.

What to do — Serve max-age=31536000; includeSubDomains; preload, verify at hstspreload.org, then submit — remembering preload is effectively irreversible for the whole domain tree.

Rate Limiting (11% of the Security score)

Rate Limiting — brute-force throttling

How it works — First the main response is checked for rate-limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After, the IETF RateLimit-* draft). Then 6 common auth/API endpoints (/login, /api/v1/auth/login, /wp-login.php, /auth/login, /signin, /api/) each receive a burst of 10 rapid, harmless requests. A 429 Too Many Requests within the burst means a limiter is active — and configured tighter than the recommended ceiling of roughly 5 requests/minute with burst 3 (the values in this product's own fix snippets). A burst accepted with no 429 and no headers produces the finding: the limit is either absent or so wide it protects nothing.

What is checked: 429 within the 10-request burst on an endpoint → pass; rate-limit headers present → pass; nothing detected → medium (−4).

Why it matters — The threshold is the whole point: 3–5 requests/minute on a login stops password spraying while a human who mistypes twice still gets in; a limit of 1000/hour technically “exists” but offers no protection. Without any limiter, credential-stuffing bots try millions of leaked username/password pairs at full link speed.

How it could be exploited — The attacker replays leaked credential pairs from a botnet; without a limiter answering 429 the only cost is bandwidth. One hit is an account takeover, and your logs show nothing but ordinary-looking successful requests — the breach is discovered by your users, not by you.

What to do — Size the limit to real human behavior — e.g. nginx limit_req_zone rate=5r/m with burst=3, a Cloudflare WAF rate-limiting rule of 5 requests/minute on /login — and answer 429 with Retry-After. Add progressive delay or CAPTCHA on auth routes for defense in depth.

Subdomain Takeover

Subdomain Takeover — dangling DNS records

How it works — Common subdomains (www, dev, staging, api, … plus your policy additions) are resolved for CNAME aliases. Each alias target is matched against fingerprints of takeover-capable services (AWS S3, Heroku, GitHub Pages, Azure Web Apps, CloudFront, Zendesk, Shopify, Firebase, GitLab/Bitbucket Pages, and more). For a matching target the service itself is probed: its unclaimed-resource page (e.g. S3's NoSuchBucket) or NXDOMAIN on the target confirms the record dangles.

What is checked: no dangling records → pass; confirmed dangling record → critical (−15); CNAME to a takeover-capable service the scan could not confirm → medium (−4, review).

Why it matters — A dangling CNAME hands your subdomain's identity to whoever registers the dead resource on the service: they can host phishing on a hostname that is genuinely yours, read cookies scoped to the subdomain, and satisfy CSP/allowlist checks that trust your domain. This is a full compromise of the subdomain, not a misconfiguration warning — and it almost always starts with an abandoned third-party integration.

How it could be exploited — The attacker finds the dead staging.example.com → dead-app.herokuapp.com record, registers dead-app on Heroku themselves, and now serves anything they want from a name under your domain: a password-harvesting login page, malicious downloads attributed to you, or scripts that abuse cookies scoped to *.example.com.

What to do — Delete the dangling DNS record (or re-create the resource on the service first). Keep an inventory of every CNAME and audit it whenever a third-party integration is retired — that is the moment takeovers are born.

Regression penalties

When we detect that a metric has dropped recently compared to its recent history (tracked by the regression monitor), the overall score is reduced as a temporary warning:

  • −10 per critical regression (e.g. "Performance score dropped from 82 to 61")
  • −5 per warning regression

Penalties are subtracted after the weighted average and the total is floored at 0. They are meant to surface a change in behavior before it becomes a full outage.

Reading the Score Breakdown

On the domain page, the Score Breakdown shows each category with its score / 100 and a "+N points" contribution. That number is how many of the composite's 100 points the category currently earns:

contribution = round(category score × weight ÷ 100)

  • With the default 25% weight, a perfect Availability score contributes +25 points to the total.
  • Because the contribution is rounded, a category at 99% can round up to +25 for the composite even though it is not a perfect 100. The "Perfect" label in the breakdown is only shown for a genuine 100.

The +N points to perfect line shows how much of that category's weight remains unearned (weight − contribution).

Interpreting the overall score

Overall score Meaning
90–100 Healthy. All measured areas are strong.
70–89 Decent, but one or more areas can be improved.
< 70 Problems. Check the breakdown and the recommendations for what to fix first.

The suggestions under the breakdown are generated from the weakest categories, so fixing the top recommendation usually moves your score the most.

A worked example

Suppose all four categories have data with these raw values:

  • Availability: 100% (score 100)
  • SSL: 45 days remaining, TLS 1.3, redirects to HTTPS (score 100)
  • Lighthouse: 90 (score 90)
  • Security: 84 (score 84)

overall = (100×25 + 100×25 + 90×25 + 84×25) / 100 = 93.5 → 93

If, a week later, Performance drops from 95 to 70 (a critical regression), the same inputs would yield:

93.5 − 10 = 83.5 → 84