How Lens Scores a Domain

Lens runs five checks in parallel and produces a single A+–F grade. This guide explains the scoring model, how email security is split between sending and receiving domains, and how to test specific DKIM selectors.

What lens checks

For any domain, lens calls five backends in parallel and aggregates the results:

SectionToolWhat it measuresWeight
IPifconfig-rsNetwork type, hosting reputation, geolocation10%
DNSprismDNSSEC, CAA records, NS delegation, HTTPS records20%
TLStlsightCertificate chain, protocol version, cipher strength35%
HTTPspectraSecurity headers, HSTS, CORS, cookie attributes20%
EmailbeaconSPF, DKIM, DMARC, MTA-STS, DANE, BIMI, DNSBL15%

HTTP and Email sections are optional. When a backend is not configured, the remaining weights are rebalanced proportionally — a missing section does not penalise the grade.

Scoring algorithm

Each check has a verdict and a weight:

A section score is earned / possible × 100%. The overall score is the weighted average of active section scores. The letter grade is determined by comparing the overall percentage to fixed thresholds:

GradeScoreMeaning
A+≥ 97%Exemplary — all checks pass
A≥ 90%Excellent — minor gaps only
B≥ 75%Good — some non-critical findings
C≥ 60%Fair — notable gaps, action recommended
D≥ 40%Poor — significant issues present
F< 40% or hard-failFailing

Hard failures

Two TLS conditions force the grade to F regardless of the numeric score:

These conditions indicate an immediately broken service, so the score is irrelevant. All other findings contribute to the numeric score rather than triggering a hard override.

Email security: sending vs. receiving

Email security has two distinct concerns that apply to different domains:

Lens reflects this split in four scored buckets within the Email section:

BucketChecksApplies toWeight
Authentication SPF, DKIM, DMARC All domains 10
Infrastructure MX, FCrDNS, DNSBL Domains with MX records 5
Transport MTA-STS, TLS-RPT, DANE Domains with MX records 5
Brand Policy BIMI, DMARC enforcement Domains with MX records 2

When lens detects no MX records, the three receiving buckets are marked not applicable and use a skip verdict. Skip verdicts are excluded from both earned and possible totals, so a parked domain is not penalised for missing receiving infrastructure. Only the Authentication bucket (weight 10) is scored.

Worked example: mail domain vs. parked domain

Mail domain (MX records present)

example.com runs email. All four buckets are scored. SPF, DKIM, and DMARC pass; MTA-STS is missing (warn); everything else passes.

Parked domain (no MX records)

parked.example.com is used only for web. No MX records exist. Infrastructure, Transport, and Brand Policy are skipped automatically.

A parked domain with correct SPF and DMARC records scores A+ on the email section — the absence of receiving infrastructure is not a deficiency.

Testing DKIM selectors

By default, beacon uses a built-in map of known DKIM selectors for major email providers (Google Workspace, Microsoft 365, etc.). For custom selectors, pass them via the dkim_selectors parameter:

# Via URL parameter
https://lens.netray.info/?d=example.com&dkim_selectors=google,selector1

# Via curl
curl -s 'https://lens.netray.info/api/check/example.com?stream=false&dkim_selectors=default,s1' \
  | jq '.email.checks[] | select(.name == "email_authentication")'

Selector validation rules:

Invalid selectors return HTTP 400 before any backend call. Absent dkim_selectors parameter: beacon uses its built-in provider map.

Section states

A section can be in one of three states:

StateEffect on scoringWhen it occurs
Scored Contributes to the overall weighted average Backend responded successfully
Errored Excluded from scoring silently Backend unreachable or timed out
Not applicable Excluded from scoring; reason recorded in summary.not_applicable Email backend timed out internally (grade: "Skipped")

An errored section does not lower the grade — the overall score is computed from whatever sections are available. A footnote appears in the UI when not_applicable is non-empty.

Cache behaviour

Results are cached per domain for 5 minutes. The X-Cache response header indicates HIT or MISS. Requests with dkim_selectors always bypass the cache, since selector-specific results cannot be shared.