Network & Monitoring
Email Auth Checker
Analyze SPF, DMARC, and DKIM email authentication records via Cloudflare DoH
Look up MX, SPF, DMARC, and common DKIM selectors for a domain using Cloudflare DNS over HTTPS, with pass/warn/fail guidance.
Features
- Accepts a bare domain or http(s) URL (hostname extracted)
- Parses SPF and DMARC policies with actionable warnings
- Probes common DKIM selectors (
google,selector1,selector2,default,k1) - Stateless; no bindings required
API Reference
GET /check
Prop
Type
Example Request
curl "https://your-worker.workers.dev/check?domain=cloudflare.com"Success Response
domain string
Normalized domain that was checked
mx array
MX records with priority and exchange
spf / dmarc / dkim object
Per-record analysis with status, details, and optional raw records
summary object
Overall status (pass | warn | fail) and issues list
{
"domain": "cloudflare.com",
"mx": [{ "priority": 10, "exchange": "isaac.mx.cloudflare.net" }],
"spf": {
"status": "pass",
"record": "v=spf1 include:_spf.mx.cloudflare.net -all",
"detail": "SPF record present"
},
"dmarc": {
"status": "pass",
"policy": "reject",
"detail": "DMARC policy is reject"
},
"dkim": {
"status": "warn",
"selectorsChecked": ["google", "selector1", "selector2", "default", "k1"],
"found": [],
"detail": "No DKIM keys found for common selectors..."
},
"summary": {
"status": "warn",
"issues": ["No DKIM keys found for common selectors..."]
}
}Error Codes
400- Missing or invaliddomain(INVALID_DOMAIN)502- DoH lookup failed (DNS_ERROR)
Use Cases
- Verify email authentication before sending from a new domain
- Spot missing SPF/DMARC during onboarding
- Explain email auth posture to non-DNS experts
- Complement raw DNS dumps with actionable analysis
Limitations
- DKIM selectors are not enumerated exhaustively; only common ones are probed
- Does not validate live mail delivery or DKIM signatures on messages
- Relies on public DoH answers; DNS TTL/caching may lag recent changes
Use in your project
Copy these files into an existing Worker. Prefer Deployment to try the full experiment first. Source: apps/experiments/email-auth-checker.
DependenciesNoneBindingsNonePlatformFetch API
import type { AuthStatus, CheckResponse, DkimResult, DmarcResult, MxRecord, SpfResult,} from "../types/check";import { DKIM_SELECTORS } from "../constants/defaults";import { queryDoh } from "./doh";function parseMx(records: string[]): MxRecord[] { return records .map((data) => { const parts = data.trim().split(/\s+/); if (parts.length < 2) return null; const priority = Number.parseInt(parts[0], 10); const exchange = parts[1].replace(/\.$/, ""); if (!Number.isFinite(priority)) return null; return { priority, exchange }; }) .filter((r): r is MxRecord => r !== null) .sort((a, b) => a.priority - b.priority);}function analyzeSpf(txtRecords: string[]): SpfResult { const spf = txtRecords.find((r) => r.toLowerCase().startsWith("v=spf1")); if (!spf) { return { status: "missing", detail: "No SPF TXT record (v=spf1) found", }; } if (/\+all\b/i.test(spf)) { return { status: "warn", record: spf, detail: "SPF ends with +all (too permissive)", }; } if (/[?]all\b/i.test(spf)) { return { status: "warn", record: spf, detail: "SPF ends with ?all (neutral; prefer ~all or -all)", }; } return { status: "pass", record: spf, detail: "SPF record present", };}function analyzeDmarc(txtRecords: string[]): DmarcResult { const dmarc = txtRecords.find((r) => r.toLowerCase().startsWith("v=dmarc1")); if (!dmarc) { return { status: "missing", detail: "No DMARC record found at _dmarc.<domain>", }; } const policyMatch = /;\s*p=([a-z]+)/i.exec(dmarc) ?? /^v=dmarc1;\s*p=([a-z]+)/i.exec(dmarc); const policy = policyMatch?.[1]?.toLowerCase(); if (!policy || policy === "none") { return { status: "warn", record: dmarc, policy: policy ?? "none", detail: "DMARC policy is none (monitoring only)", }; } return { status: "pass", record: dmarc, policy, detail: `DMARC policy is ${policy}`, };}async function analyzeDkim(domain: string): Promise<DkimResult> { const found: DkimResult["found"] = []; await Promise.all( DKIM_SELECTORS.map(async (selector) => { const name = `${selector}._domainkey.${domain}`; try { const records = await queryDoh(name, "TXT"); const dkim = records.find((r) => /v=DKIM1/i.test(r) || /p=/i.test(r)); if (dkim) { found.push({ selector, found: true, record: dkim }); } else { found.push({ selector, found: false }); } } catch { found.push({ selector, found: false }); } }) ); const hits = found.filter((f) => f.found); if (hits.length === 0) { return { status: "warn", selectorsChecked: [...DKIM_SELECTORS], found, detail: "No DKIM keys found for common selectors (google, selector1, selector2, default, k1). Other selectors may still exist.", }; } return { status: "pass", selectorsChecked: [...DKIM_SELECTORS], found, detail: `Found DKIM for selector(s): ${hits.map((h) => h.selector).join(", ")}`, };}function summarize( mx: MxRecord[], spf: SpfResult, dmarc: DmarcResult, dkim: DkimResult): CheckResponse["summary"] { const issues: string[] = []; if (mx.length === 0) issues.push("No MX records"); if (spf.status === "missing" || spf.status === "fail") issues.push(spf.detail); else if (spf.status === "warn") issues.push(spf.detail); if (dmarc.status === "missing" || dmarc.status === "fail") issues.push(dmarc.detail); else if (dmarc.status === "warn") issues.push(dmarc.detail); if (dkim.status === "warn" || dkim.status === "fail" || dkim.status === "missing") { issues.push(dkim.detail); } let status: AuthStatus = "pass"; if (mx.length === 0 || spf.status === "missing" || dmarc.status === "missing") { status = "fail"; } else if ( spf.status === "warn" || dmarc.status === "warn" || dkim.status === "warn" || dkim.status === "missing" ) { status = "warn"; } return { status, issues };}/** * Look up MX/TXT/DMARC/DKIM via DoH and analyze email authentication posture. */export async function checkEmailAuth(domain: string): Promise<CheckResponse> { const [mxRaw, txtRaw, dmarcRaw] = await Promise.all([ queryDoh(domain, "MX"), queryDoh(domain, "TXT"), queryDoh(`_dmarc.${domain}`, "TXT"), ]); const mx = parseMx(mxRaw); const spf = analyzeSpf(txtRaw); const dmarc = analyzeDmarc(dmarcRaw); const dkim = await analyzeDkim(domain); return { domain, mx, spf, dmarc, dkim, summary: summarize(mx, spf, dmarc, dkim), };}// Exported for unit testsexport { parseMx, analyzeSpf, analyzeDmarc };Deployment
Deploy
Follow the deployment wizard. No bindings or secrets required.
Test your deployment
curl "https://your-worker.workers.dev/check?domain=example.com"Local Development
cd apps/experiments/email-auth-checker
npm install
npm run devcurl "http://localhost:8787/check?domain=cloudflare.com"Cloudflare Features Used
- Workers - Edge compute runtime
- DNS over HTTPS - Cloudflare DoH JSON API