DomainScores Methodology — How We Grade Domain Security

The DomainScores grading rubric: how we weight DNS, email authentication, TLS, and web security checks into a single A–F domain security score.

Version v1.0 · Last updated: 21 June 2026 · This page is canonical. Material changes are announced 7 days in advance and recorded in the changelog below.

1. How the grade works

Each scan produces a single letter grade from this set: A+, A, B, C, D, F, plus three explicit unscoreable states: X (registered but DNS unreachable), Z (NXDOMAIN — the name no longer exists), U (still being classified). The grade is a deterministic function of the per-check results. There is no machine learning, no reputation lookup, and no human override.

The grade calculation in plain terms:

Note on weights: weights are calibrated against the production dataset so that the global grade distribution is bell-shaped (not bimodal). The current calibrated weight table is published alongside this page once the launch dataset is sealed.

  • Each of the 34 checks returns pass, fail, or not_applicable. A not_applicable result is excluded from scoring (it does not count against the domain).
  • Each check has a published weight. We compute the percentage score as (weighted pass total) ÷ (weighted applicable total).
  • Letter grades map from the percentage: A+ ≥ 97, A ≥ 90, B ≥ 80, C ≥ 70, D ≥ 60, F < 60.
  • Three checks act as automatic-F triggers if they fail, regardless of the percentage score: cert-valid (TLS certificate cannot be validated), https-available (the domain serves no HTTPS at all), and dnssec-broken (DNSSEC chain is advertised but mismatched — note that absence of DNSSEC is not an auto-F, only a broken chain is, as broken DNSSEC is a security regression).

2. The X / U / Z taxonomy

Three states sit outside the A+→F scale because they describe domains where we honestly can't produce a security grade:

The point of X / U / Z is to refuse to fake a grade for a domain we couldn't actually measure. Conflating "didn't respond" with "got an F" hides real security information.

  • X — Registered but unreachable. The domain is registered (NS records exist at the parent zone) but its own authoritative nameservers don't answer A / AAAA queries within a reasonable budget (we cascade through 2s → 4s → 8s → 16s timeouts before declaring X). From an attacker's perspective this domain is a spoofable shell: no email/web defenses are in front, but the name is still owned and resolvable at the parent. We grade X rather than F because F implies "we scanned you and you scored badly"; X is honestly "we couldn't reach you to scan."
  • Z — Dead (NXDOMAIN). The name no longer resolves at the parent. The registration has lapsed or the domain has been delegated to nothing. Effectively, the domain is gone.
  • U — Unscored (in-flight). The classifier is still working through its cascade and hasn't reached a terminal state yet. U is always temporary; you should never see a U on a domain check at the homepage — the homepage waits for a terminal state.

3. The 34 checks

All 34 production checks, by category. Each check is binary (pass / fail / N/A) and individually reproducible from the per-check result API exposed on the domain's grade page.

DNS (8 checks)

caa-record

CAA DNS record present, restricting which CAs can issue certificates

dnssec-ds

DS record present at the parent zone

dnssec-dnskey

DNSKEY present and consistent with DS (auto-F if mismatched)

cdn-waf-detection

Reachable via a recognised CDN / WAF (informational)

hosting-provider

Hosting provider identifiable (informational)

ipv6-support

Domain resolves to IPv6 (AAAA records)

nameserver-diversity

At least two nameservers in different AS / network paths

soa-configuration

SOA record present and within recommended TTL ranges

Email authentication (7 checks)

mx-record

MX records present (or explicit null MX, indicating no mail)

spf-exists

SPF record at apex

spf-policy-strength

SPF terminates in -all (or ~all with valid alignment)

dkim-exists

DKIM signing key discoverable via common selector probes

dmarc-policy

DMARC record with p=quarantine or p=reject

dmarc-reporting

DMARC specifies rua and / or ruf reporting addresses

reverse-dns

PTR records exist for MX target IPs, matching forward records

TLS / certificates (10 checks)

https-available

HTTPS served on port 443 with a successful handshake (auto-F if no)

cert-valid

Leaf certificate validates against the public PKI for the scanned hostname (auto-F if no)

cert-expiry-warning

Certificate expires more than 14 days from scan time

cert-signature-algorithm

Modern signature algorithm (SHA-256 or stronger)

cert-key-strength

Public key meets minimum strength (RSA ≥ 2048, ECDSA ≥ 256)

tls-version

At least TLS 1.2 supported; TLS 1.3 preferred

cipher-strength

Negotiated cipher suite is modern; no NULL / EXPORT / RC4 / DES

tls-compression

TLS compression disabled (CRIME mitigation)

ocsp-stapling

Server supports and uses OCSP stapling

secure-renegotiation

RFC 5746 secure renegotiation supported

Web security headers (6 checks)

http-to-https-redirect

HTTP redirects to HTTPS via 301 / 308

hsts-header

HSTS present, max-age ≥ 6 months recommended

csp-header

Content-Security-Policy present with non-trivial policy

x-frame-options

X-Frame-Options or CSP frame-ancestors set to deny / sameorigin

x-content-type-options

X-Content-Type-Options: nosniff

referrer-policy

Referrer-Policy present and not unsafe-url

WHOIS / RDAP (3 checks)

Some TLD registries (e.g. .ie, .dk) redact WHOIS / RDAP data by policy. For those, the WHOIS checks return not_applicable rather than fail.

domain-expiry

Registration expires more than 30 days from scan time

registrar-transfer-lock

Registrar transfer lock (clientTransferProhibited) set

whois-privacy

WHOIS / RDAP returns privacy-protected contact info (informational, no penalty)

4. How not_applicable works

Several checks legitimately don't apply to certain domains. We exclude them from scoring rather than penalising:

A domain that has no AAAA records does not get not_applicable on ipv6-support — that check is a deliberate signal we expect by 2026, and absence is scored as fail.

  • A domain with a null MX record (0 .) explicitly declares no mail service — all seven email checks return not_applicable, and the grade is computed on the remaining 27 checks.
  • A subdomain that has no apex SPF / DMARC record but has those records at the apex inherits the apex result.
  • WHOIS / RDAP checks return not_applicable for TLDs where the registry redacts contact data by policy.

5. What we don't check

The rubric measures publicly observable infrastructure hygiene. It deliberately does not score:

For any of the above, DomainScores is the wrong tool. Use a vulnerability scanner, a content classifier, a reputation service, or an accessibility audit respectively.

  • Application-layer vulnerabilities (XSS, SQLi, auth issues). DomainScores is not a vulnerability scanner.
  • Site content (malware, phishing pages, dubious topics). DomainScores is not a content classifier.
  • Reputation (blocklist membership, abuse-report aggregates). The rubric is non-reputational by design.
  • Privacy practices, cookie consent, accessibility, or other regulatory compliance topics.
  • Anything that requires an authenticated request or interactive session.

6. Coverage — which TLDs we assess

DomainScores grades domains across the world's scannable namespace — every TLD whose zone we can enumerate. We build that population from two authoritative sources: ICANN's Centralized Zone Data Service (CZDS) for generic TLDs (.com, .org, and ~1,080 others), and the OpenINTEL measurement project for country-code TLDs (.ie, .uk, .de, and ~300 others) — together 1,386 TLDs covering roughly 330 million delegated domains.

We assess delegated domains only — names with live NS records at the parent zone. This is why our per-TLD totals run below registry "domains-under-management" figures, which also count registered-but-undelegated and parked names: a domain with no nameserver has nothing for a security scan to reach.

51 TLDs sit outside our coverage, for one of four reasons. In every case there is effectively nothing to assess, so the exclusion costs negligible real-world coverage:

We re-evaluate this list as registries change their zone-access policies; coverage changes are recorded in the changelog below.

  • Infrastructure & restricted-sponsored (.arpa, .edu, .int, .mil) — these publish no enumerable zone. .arpa is reverse-DNS infrastructure; the others are closed sponsored namespaces.
  • Dormant country codes (.bv, .gb, .sj) — Bouvet Island and Svalbard / Jan Mayen accept no registrations, and .gb is the deprecated legacy UK code, replaced by .uk. Effectively zero domains.
  • Internationalised (IDN) TLDs without a published list (13) — non-Latin-script TLDs that expose no domain list through either source.
  • Single-brand & restricted generic TLDs (31 — e.g. dot-brands like .abbott or .williamhill, and sponsored TLDs like .post) — near-empty zones, often a single registry hostname, not available to us through CZDS.

7. Versioning & changelog

This is methodology version v1.0. Any future change to the check list (adding, removing, redefining), the weight table, the grade thresholds, or the auto-F trigger list will be announced here with a 30-day notice before the change takes effect. The version that produced any historical grade is recorded with the result, so a domain owner can always trace why their grade changed.

Changelog
DateVersionChange
2026-06-21v1.0Added §6 (Coverage): documented the CZDS + OpenINTEL source population, the delegated-domains-only scope, and the 51 TLDs excluded from assessment (infrastructure, dormant ccTLDs, listless IDNs, near-empty dot-brands). No change to checks, weights, thresholds, or grades.
2026-05-20v1.0Initial public release. 34 checks across DNS (8), email (7), TLS (10), web headers (6), WHOIS (3). X / U / Z unscoreable states defined.

8. Disputing a grade

If you believe a grade is incorrect:

The scanning policy (source IPs, opt-out, abuse reporting) is at /scanning.

  • Apply the fix to your domain (e.g. add the missing DMARC record).
  • Visit the grade page for your domain and request a re-scan. Re-scans complete within 24 hours; the grade refreshes within 7 days.
  • If the re-scan returns the same grade and you believe the check itself is wrong, write to [email protected] with the specific check ID, your domain, and what you observed. We commit to a written response within 7 days. Substantive methodology critiques are published in the changelog above.

Contacts