SPF synthesis, explained. No hand-waving.
Here is exactly how UglyDMARC defeats the 10-lookup limit, how the DMARC reporting pipeline works, and what happens at every step between a sending MTA and a receiver's SPF evaluation.
SPF synthesis — the 10-lookup problem, solved
RFC 7208 limits SPF to 10 DNS-resolving mechanisms per evaluation. UglyDMARC re-architects how answers are generated so the receiver only ever needs one.
You publish one include on your domain
Replace every vendor include in your SPF record with a single UglyDMARC macro include. That's the only change you make to your DNS.
Your new SPF record
Publish this on your domain (e.g. acme-corp.com):
# Before: vendor soup that blows the limit v=spf1 include:_spf.google.com include:spf.protection.outlook.com include:sendgrid.net include:mailgun.org include:amazonses.com include:_spf.salesforce.com include:mail.zendesk.com -all ^ 7+ includes, each expands to more lookups # After: one include, all senders handled v=spf1 include:%{ir}.%{v}.%{d}.spf.uglydmarc.com -all
What the macros mean
When a receiving MTA evaluates this record, it expands the macros before querying. For sender IP 1.2.3.4 sending as acme-corp.com:
%{ir}→ sender IP reversed:4.3.2.1%{v}→ IP version token:in-addr%{d}→ sending domain:acme-corp.com
The resulting query is:
4.3.2.1.in-addr.acme-corp.com.spf.uglydmarc.com
UglyDMARC pre-flattens your entire include tree
Before any MTA ever queries us, a background DAG builder recursively resolves every include:, ip4:, ip6:, and a: mechanism in your chain — across every vendor — into a flat, deduplicated CIDR set stored in cache.
Always fresh
The builder watches TTLs and refreshes each node before it expires. When Google or SendGrid updates their IP ranges, UglyDMARC picks it up automatically — no manual action required.
Never in the query path
The DAG builder is strictly background. It never runs during a DNS query. Query-time is always O(1) — a single CIDR membership test against the cached set.
# What the background builder resolves for you # (all happening before any MTA queries) acme-corp.com → _spf.google.com → 35.190.247.0/24 → 64.233.160.0/19 → 66.102.0.0/20 → ... 12 more ranges acme-corp.com → spf.protection.outlook.com → 40.92.0.0/15 → 40.107.0.0/16 → ... 8 more ranges acme-corp.com → sendgrid.net → 167.89.0.0/17 → 198.37.144.0/20 → ... 4 more ranges # Flattened result: deduplicated CIDR set spf:domain:acme-corp.com:dag → 47 CIDRs, cached
At query time: one lookup, one answer, always clean
When the receiving MTA queries for a specific sender IP, UglyDMARC's synthesizer does a single CIDR membership test against the pre-built set and returns the appropriate answer immediately.
# IP in the cached CIDR set → pass 4.3.2.1.in-addr.acme-corp.com.spf.uglydmarc.com. IN TXT "v=spf1 ip4:1.2.3.4 -all" # IP NOT in the set → softfail (unknown sender) 8.7.6.5.in-addr.acme-corp.com.spf.uglydmarc.com. IN TXT "v=spf1 -all"
The result
The receiver evaluates exactly 1 DNS lookup — the top-level include — regardless of how many vendors are in your tree. No permerror. No SPF failures caused by lookup count. Deterministic, always.
What never happens at query time
- No live DNS resolution — ever
- No recursive SPF evaluation
- No blocking on a cache rebuild
- No network calls outside the cache lookup
What does happen
- Parse the qname to extract the sender IP and domain
- Load the domain's pre-built CIDR set from cache
- O(1) CIDR membership test
- Return a minimal synthesized TXT record
P99 query latency: < 2 ms
The DMARC reporting pipeline
SPF synthesis solves the lookup problem. DMARC reporting solves the visibility problem — and together they give you a safe, data-driven path to p=reject.
Reports come straight to UglyDMARC
You set your domain's DMARC rua= to a UglyDMARC reporting address. From then on, mailbox providers (Google, Microsoft, Yahoo, and others) send their daily aggregate reports directly to UglyDMARC — there's no mailbox of yours to connect and no MX changes. We receive every report and process it for you.
From raw XML to a clear picture
Reports arrive as gzip-compressed XML. UglyDMARC parses every record — sender IP, SPF result, DKIM result, DMARC alignment and disposition, and the sending source — then normalizes and stores it for display in UglyDMARC.
# What a DMARC aggregate report tells you (simplified) # Reporter: google.com | Period: 2026-06-01 Source IP Count SPF DKIM DMARC 192.0.2.10 1,482 pass pass pass ← Google Workspace, all good 198.51.100.20 341 pass pass pass ← SendGrid, configured correctly 203.0.113.42 12 fail fail fail ← unknown sender — investigate 203.0.113.99 3 fail fail fail ← potential spoofing attempt # Without this data, you can't safely move to p=reject
SPF & DKIM pass rates
Per-domain charts for SPF pass, DKIM pass, and DMARC alignment over time. See trends by reporter, by source IP, and by disposition.
Search across everything
Cross-domain record search with predicate filters — find any IP, ESP, or sending source across every domain in your account. Export to CSV.
Anomaly detection
Get notified when a new unknown sender appears, pass rates drop, or your DMARC policy is overriding SPF failures. Route alerts to email or webhook.
From p=none to p=reject, with data
Most domains stay at p=none indefinitely because enforcing is too risky without visibility. UglyDMARC closes that gap.
p=none — observe
Turn on DMARC reporting. Every ISP starts sending you aggregate data. No mail is affected. UglyDMARC collects and parses all of it.
p=quarantine — tighten
Once you've identified all legitimate senders and resolved SPF issues (with synthesis handling the lookup limit), move to quarantine. Monitor for false positives.
p=reject — enforce
When DMARC reports show all your legitimate mail passing, flip to reject. Spoofed mail is rejected at the receiver. Your domain is protected.
Add your domain and publish one record.
Setup takes minutes. No agents, no MX changes, no complex configuration — just add your domain and update one DNS record.
Get started ↗