Methodology, version 1
How the domain security grade works
The security grade (A+ to F) measures how far a domain follows current standards for email, DNS, the web and IPv6. It is worked out from the same checks as the domain health report, with no extra lookups, and every point is listed on this page.
It is not the report’s health score: that one also counts registration, blocklists and certificate expiry, things that can break a domain but are not a matter of standards. The grade is about what a domain has set up, and is what the badge shows.
The scale
| Grade | Score |
|---|---|
| A+ | 95–100 |
| A | 85–94 |
| B | 70–84 |
| C | 55–69 |
| D | 40–54 |
| F | 0–39 |
- A+ also needs every category that applies to score at least 90; otherwise a score of 95 or more is an A.
- Some basics hold the score at 69 (a C) however good the rest is, when they fail outright:
- The website has no HTTPS with a trusted certificate.
- There is no working SPF record.
- There is no working DMARC record.
How the score adds up
- Each check earns all of its points, part of them, or none.
- A category scores the share of the points it could earn, out of 100. Checks that do not apply to the domain (mail checks for a domain without mail servers, web checks for a domain without a website) are left out, so they neither help nor hurt.
- The overall score is the weighted mean of the categories that apply: Email security 35% Web security 35% DNS 15% Modern protocols 15% A category that does not apply hands its weight to the others.
- Then the caps and the A+ rule above.
If a check could not run (a server timed out, the report ran out of time), the result page still shows a provisional grade, but it is never stored for the badge: the badge keeps the last complete grade.
Email security weight 35
| Check | Points | Full points | Counts for |
|---|---|---|---|
| SPF record | 20 |
One SPF record ending in -all or ~all (or handing over with redirect=), within the 10-lookup limit.
Half the points: it ends in ?all or has no "all" at all, so it protects nothing. Fix: Publish one SPF record that lists your senders and ends in ~all or -all. A domain that sends no mail: v=spf1 -all.
|
Every domain |
| DMARC policy | 30 |
One DMARC record with p=reject for all mail.
p=quarantine earns 25 points, p=none 10. A pct= below 100 counts one step lower. Fix: Publish a DMARC record, read its reports, then move from p=none to quarantine and on to reject.
|
Every domain |
| DMARC reports | 5 |
The DMARC record asks for aggregate reports (rua=mailto:).
Fix: Add rua=mailto:an-address-you-read to the DMARC record, so you see who sends as your domain.
|
Every domain |
| MTA-STS | 15 |
An MTA-STS record and a policy file in enforce mode.
About half the points (7) in testing mode. Fix: Publish an MTA-STS policy at https://mta-sts.<domain>/.well-known/mta-sts.txt and the _mta-sts TXT record; start in testing mode, then enforce.
|
Domains that receive mail (MX) |
| TLS-RPT | 10 |
A TLS-RPT record (_smtp._tls), so senders report failed TLS connections to you.
Fix: Publish _smtp._tls.<domain> TXT "v=TLSRPTv1; rua=mailto:tls-reports@<domain>".
|
Domains that receive mail (MX) |
| Null MX | 10 |
A domain without mail servers says so with a null MX record (RFC 7505).
Fix: If the domain receives no mail, publish <domain>. MX 0 . so senders stop trying.
|
Domains without mail servers |
Web security weight 35
| Check | Points | Full points | Counts for |
|---|---|---|---|
| HTTPS with a trusted certificate | 30 |
Every website host checked (the domain and www) has a trusted, unexpired certificate for its name.
Fix: Install a certificate from a public CA (Let’s Encrypt is free) that covers the domain and www, with the full chain.
|
Domains with a website |
| TLS 1.3 | 10 |
The website negotiates TLS 1.3.
Fix: Turn on TLS 1.3 in the web server or CDN (keep 1.2 for older clients, turn off 1.0 and 1.1).
|
Domains with a website |
| HTTP redirects to HTTPS | 15 |
http:// requests are redirected to https://.
About half the points (8) when plain HTTP does not answer at all: safe, but typed addresses fail. Fix: Answer on port 80 and redirect every request to https:// with a 301.
|
Domains with a website |
| HSTS | 15 |
A Strict-Transport-Security header with a max-age of at least 180 days.
About half the points (7) with a shorter max-age. Fix: Send Strict-Transport-Security: max-age=31536000; includeSubDomains on every HTTPS response.
|
Domains with a website |
| HSTS preload-ready | 5 |
The HSTS header has max-age of a year or more, includeSubDomains and preload. (We check the header, not the browsers’ preload list.)
Fix: Once every subdomain serves HTTPS, add includeSubDomains; preload and submit the domain at hstspreload.org.
|
Domains with a website |
| Content-Security-Policy | 10 |
The page sends a Content-Security-Policy header.
Fix: Add a Content-Security-Policy; test it first with Content-Security-Policy-Report-Only.
|
Domains with a website |
| X-Content-Type-Options | 5 |
X-Content-Type-Options: nosniff.
Fix: Send X-Content-Type-Options: nosniff.
|
Domains with a website |
| Clickjacking protection | 5 |
X-Frame-Options DENY or SAMEORIGIN, or frame-ancestors in the CSP.
Fix: Add frame-ancestors to the CSP, or send X-Frame-Options: SAMEORIGIN.
|
Domains with a website |
| Referrer-Policy | 5 |
The page sends a Referrer-Policy header.
Fix: Send Referrer-Policy: strict-origin-when-cross-origin (or stricter).
|
Domains with a website |
DNS weight 15
| Check | Points | Full points | Counts for |
|---|---|---|---|
| DNSSEC | 40 |
The zone is signed, the parent has its DS record and answers validate.
Fix: Turn on DNSSEC at your DNS host, then add the DS record at your registrar.
|
Every domain |
| CAA record | 20 |
A CAA record names the certificate authorities allowed to issue for the domain.
Fix: Add a CAA record for the CA you use, for example <domain>. CAA 0 issue "letsencrypt.org".
|
Every domain |
| Nameserver diversity | 40 |
Two or more nameservers, in more than one network (/24), as RFC 1034 and RFC 2182 ask.
Half the points when every nameserver is in one network. Fix: Use at least two nameservers in separate networks: an anycast DNS host, or a secondary DNS provider.
|
Every domain |
Modern protocols weight 15
| Check | Points | Full points | Counts for |
|---|---|---|---|
| Website over IPv6 | 60 |
The website has an IPv6 address (AAAA on the domain or www).
Fix: Add an AAAA record for the website; most hosts and CDNs give you an IPv6 address for free.
|
Domains with a website |
| Mail over IPv6 | 40 |
At least one mail server (MX) has an IPv6 address.
Fix: Ask your mail provider for IPv6, or add AAAA records to your own mail servers.
|
Domains that receive mail (MX) |
What is not graded
- DKIM: selectors can have any name, so a key cannot be proven missing from outside. The report looks for common selectors but the grade ignores DKIM.
- Blocklists, registration and certificate expiry: they are in the health score, not the grade.
- Subdomains: only the domain and its www are checked.
- The HSTS preload list itself: we check the header asks for preloading, not whether browsers ship it yet.
The badge
Every graded domain gets a badge you can put in a README or a website footer. The result page of a domain report has the snippets to copy:
[](https://thenetworkery.com/grades/example.com)
- A badge never runs a check. It shows the latest complete grade we have stored, and links to that domain’s grade page with its history.
- A grade is stored when someone runs a domain report (one per domain per day, the last one counts). Badges that are still being fetched are re-checked in the background about once a week, a few domains an hour.
- A domain that has never been graded gets a grey “not graded” badge.
- Badges are cached for up to 15 minutes, so a new grade can take that long to show.
- The same data as JSON, for scripts:
https://thenetworkery.com/badge/example.com.json.
Changes
When checks or weights change, the methodology version goes up. Stored grades keep the version that made them, and the grade page says when a grade predates the current rules.
- Version 1 (November 2026): first published.