Skip to content
· by Femke van der Berg

SPF 10-lookup limit fix in 2026: subdomain delegation, DIY automated flattening, and when paid services are worth it

The 10-lookup limit defined by RFC 7208 is the most common cause of silent SPF authentication failures. This post covers the four real fix strategies (pruning, subdomain delegation, DIY flattening, managed services), 3-year TCO comparison, an interactive decision tool, complete day-by-day migration runbook, monitoring patterns post-fix, and the antipatterns we see in production.

spfemail-authenticationdnsdeliverabilityspf-flatteningsubdomain-delegation

The SPF 10-lookup limit defined in RFC 7208 is the single most common cause of silent SPF authentication failures we see in deliverability audits. The pattern is consistent: the operator added vendors over the years (Salesforce, HubSpot, Mailgun, SendGrid, Zendesk, Stripe, etc.) until the SPF record exceeded 10 lookups. From that point, recipients see PermError, mail fails authentication, and there is no visible error message in the bounce. The mail just doesn’t reach the inbox.

The honest version: there are 4 real fix strategies, each with operational tradeoffs that paid-service marketing rarely discusses. This post covers all four with real cost numbers, a complete migration runbook, an interactive decision tool, and the antipatterns we see in production.

Why the limit exists — understanding RFC 7208

RFC 7208 (the SPF specification) caps the number of DNS lookups during SPF evaluation at 10 nested lookups. The reason is denial-of-service prevention: an SPF record with infinite recursion would force receivers to perform unbounded DNS queries per incoming message. The 10-lookup limit puts a hard ceiling on the work a receiver has to do to validate a single message.

What counts as one lookup:

  • Each include: directive (1 lookup, plus any nested include: inside it)
  • Each a and mx mechanism (1 lookup each, plus the lookup of any A/MX record they reference)
  • Each redirect= modifier (1 lookup, plus any chain it triggers)
  • Each exists: mechanism (1 lookup)

What does NOT count: ip4:, ip6:, all, ptr (deprecated, don’t use it), and exp= modifier.

The trap operators fall into is nested includes. When you add include:_spf.salesforce.com, that’s 1 lookup, but Salesforce’s record itself can include additional services that consume more lookups. A common case: include:_spf.google.com consumes 4 lookups all by itself because Google nests includes for various sub-services. Add SendGrid (3 nested), Mailgun (2), HubSpot (2), and you’re at 11 lookups easily without realizing.

When the receiver hits the 10-lookup limit, it returns PermError (permanent error). The message is treated as failing SPF for DMARC alignment purposes. If your DMARC policy is p=quarantine or p=reject, mail goes to spam or is rejected outright. The cruel detail: there is no useful diagnostic in the bounce; only DMARC RUA reports show the PermError, and only if you’re consuming them.

The 4 options — before/after SVG diagram

There are 4 real paths to fix the SPF lookup limit, each with distinct tradeoffs:

SPF fix — comparison of 4 strategies with before and after lookup counts⚠ INITIAL STATE — PermError activeSPF record for yourdomain.com with 14 lookups (exceeds 10-lookup limit)v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org include:salesforce.com include:hubspot.com include:zendesk.com → 14 total lookups, PermError, all mail fails SPF authenticationOption 1 — PruningQuick triageAudit and remove unusedRemove servicesno longer in use→ Result: 8 lookupsPros and cons+ Free, immediate+ No dependencies– Doesn’t scaleOption 2 — SubdomainMost robust DIYDistribute by subdomainmail.yourdomain.com transactionalnews.yourdomain.com marketing→ Each subdomain: 10 lookupsPros and cons+ Free and scalable+ Sustainable long-term– Requires coordinationOption 3 — DIY FlatteningCron-based automationReplace includes with IPsPython script resolves IPsUpdates DNS via API→ 0 includes, only ip4/ip6Pros and cons+ Total control+ No recurring cost– Continuous maintenanceOption 4 — ManagedManaged SPF serviceVendor automates itAutoSPF, DMARCLY,PowerDMARC PowerSPF→ Complexity outsourcedPros and cons+ No maintenance+ Auto IP updates– $25-$400/month recurring📊 Operational comparison of 4 optionsSetup time:1-2 hours8-16 hours16-32 hours30 minutesSetup cost:$0$0$0 (engineer hours)$0-$100Recurring cost:$0$0$0 + maintenance$25-$400/monthMaintenance:Quarterly auditAnnual reviewDaily cron + monitoringVendor monitorsVendor dependency:NoneNoneNoneTotalScalability:PoorExcellentGoodExcellentIdeal use case:Quick triageMost senders 2026DIY technical teamsNo DevOps capacityRecommendation: subdomain delegation is the best long-term option for 80% of senders. Managed service when no DevOps capacity is available.

Five critical observations from the comparison. First, subdomain delegation is free, scalable, and vendor-independent — the combination most operators don’t consider because managed services dominate search engine marketing. Second, pruning is only valid as triage — it buys time but does not solve underlying scaling. Third, DIY flattening requires daily monitoring because vendor IPs change frequently — and that is precisely where managed services contribute real value. Fourth, managed service costs accumulate — $25-$400/month over 12 months is $300-$4,800/year recurring, a figure that rivals the engineering investment of subdomain delegation. Fifth, DMARC relaxed alignment (the default) allows the Return-Path to use a subdomain while the From keeps the parent domain — and that is the key piece that makes subdomain delegation viable.

3-year TCO comparison — what each option really costs

Real cost comparison over a 3-year period:

3-year TCO in USD by SPF fix method — includes engineering hours plus recurring costs
Engineering hour valued at $80. Annualized managed costs: $25/month basic, $200/month pro, $400/month enterprise. DIY includes 4-8 hours of monthly continuous maintenance.
Categoría Initial setup (engineer hours in USD)3-year recurring cost in USD
Pruning 120720
Subdomain delegation 12801440
DIY Flattening 25607680
Managed basic ($25/mo) 0900
Managed pro ($200/mo) 07200
Managed enterprise ($400/mo) 014400

Three critical TCO observations. First, subdomain delegation is the cheapest sustainable solution ($2,720 total over 3 years) and combines zero recurring cost with scalability. Second, DIY flattening has the highest continuous cost because maintenance dominates ($7,680 over 3 years, all in engineer hours). Third, the enterprise managed tier ($14,400 over 3 years) rarely justifies its cost vs the equivalent engineering investment in subdomain delegation, provided you have DevOps capacity.

Decision tool — which method fits your situation

Rather than reading another flowchart, use the following decision tool. It implements the same scoring logic we apply during deliverability audits when evaluating SPF fix strategy:

The tool considers six dimensions (current lookup count, number of services, DevOps capacity, budget, infrastructure stability, operational criticality) and produces a calibrated method recommendation plus implementation actions, tradeoffs, and post-fix monitoring requirements.

Subdomain delegation — the method rarely covered well

Subdomain delegation is the SPF fix that most online guides skip because it doesn’t sell anything. The technique works because DMARC relaxed alignment (the default mode) considers the organizational domain rather than the exact From domain when evaluating SPF alignment. This means: as long as the Return-Path uses a subdomain of your organizational domain, SPF passes for DMARC purposes without requiring exact match.

Concrete implementation:

  1. Identify service categories. Group your sending services by purpose:

    • Transactional (account notifications, password resets, order confirmations) → Postmark, SendGrid Transactional, AWS SES
    • Marketing (newsletters, promotions, drip campaigns) → Mailgun, MailerLite, Mailchimp
    • Sales/CRM (cold outreach, follow-ups) → Salesforce, HubSpot, Apollo
    • Support (helpdesk replies, ticket notifications) → Zendesk, Help Scout, Intercom
  2. Create one subdomain per category:

    mail.yourdomain.com       → Transactional services
    marketing.yourdomain.com  → Marketing services
    sales.yourdomain.com      → CRM/sales services
    support.yourdomain.com    → Helpdesk services
  3. Configure independent SPF records on each subdomain:

    mail.yourdomain.com.       TXT  "v=spf1 include:spf.postmarkapp.com include:_spf.amazonses.com -all"
    marketing.yourdomain.com.  TXT  "v=spf1 include:mailgun.org include:_spf.mlsend.com -all"
    sales.yourdomain.com.      TXT  "v=spf1 include:salesforce.com include:_spfa.hubspot.com -all"
    support.yourdomain.com.    TXT  "v=spf1 include:zendesk.com -all"

    Each subdomain has its own independent 10-lookup budget. With 4 subdomains you have 40 effective lookups.

  4. Configure each vendor with the corresponding Return-Path subdomain. This is the operational step that requires vendor coordination — each vendor has its own UI/process to configure custom Return-Path:

    • Postmark: Server settings → Return-Path domain → Custom subdomain
    • Mailgun: Domains → Add domain → marketing.yourdomain.com
    • Salesforce: Email Deliverability → Return Path → Custom domain
  5. Validate DMARC relaxed alignment:

    _dmarc.yourdomain.com.  TXT  "v=DMARC1; p=quarantine; aspf=r; adkim=r; rua=mailto:[email protected]"

    aspf=r (relaxed) is the default — keep it. With aspf=s (strict), this approach wouldn’t work.

  6. Test before committing. Send test emails through each service, confirm Authentication-Results header shows spf=pass smtp.mailfrom=mail.yourdomain.com and DMARC passes via SPF alignment.

The setup typically takes 8-16 engineer hours for a sender with 5-7 services. The maintenance afterward is essentially zero — only when you add new services or change vendors do you need to update the corresponding subdomain.

DIY automated flattening — real code that works

If subdomain delegation isn’t viable (some vendors don’t support custom Return-Path subdomains, or operational coordination is impossible), DIY automated flattening is the next-best option. The principle: replace include: directives with explicit ip4:/ip6: blocks resolved dynamically from vendor SPF records.

A working Python script that does the job:

#!/usr/bin/env python3
"""
spf-flatten.py — resolve all includes in an SPF record to literal IPs
and update the flattened record in DNS via Cloudflare API.

Run as cron daily; alerts via webhook if flattened record exceeds 9 lookups.
"""

import dns.resolver
import json
import requests
import sys
from typing import List, Set

# Configuration
DOMAIN = "yourdomain.com"
INCLUDES_TO_FLATTEN = [
    "_spf.google.com",
    "sendgrid.net",
    "mailgun.org",
    "_spfa.hubspot.com",
    "salesforce.com",
]
ADDITIONAL_IPS = [
    "ip4:203.0.113.0/24",  # Your own SMTP servers
]
CF_TOKEN = "your_cloudflare_api_token"
CF_ZONE_ID = "your_zone_id"
ALERT_WEBHOOK = "https://hooks.slack.com/services/..."

def resolve_spf_ips(domain: str, depth: int = 0, seen: Set[str] = None) -> List[str]:
    """Recursively resolve an SPF record to all its ip4/ip6 mechanisms."""
    if seen is None:
        seen = set()
    if domain in seen or depth > 10:
        return []
    seen.add(domain)
    
    ips = []
    try:
        answers = dns.resolver.resolve(domain, "TXT")
    except Exception as e:
        print(f"WARN: Cannot resolve TXT for {domain}: {e}", file=sys.stderr)
        return []
    
    for rdata in answers:
        record = b"".join(rdata.strings).decode()
        if not record.startswith("v=spf1"):
            continue
        for token in record.split():
            if token.startswith("ip4:") or token.startswith("ip6:"):
                ips.append(token)
            elif token.startswith("include:"):
                included = token[8:]
                ips.extend(resolve_spf_ips(included, depth + 1, seen))
            elif token.startswith("redirect="):
                redirected = token[9:]
                ips.extend(resolve_spf_ips(redirected, depth + 1, seen))
    return ips

def build_flattened_record(ips: List[str], extra: List[str]) -> str:
    """Build a flattened SPF record with deduplication."""
    unique_ips = sorted(set(ips + extra))
    return "v=spf1 " + " ".join(unique_ips) + " -all"

def update_cloudflare_dns(record: str) -> None:
    """Update the SPF TXT record at Cloudflare via API."""
    headers = {"Authorization": f"Bearer {CF_TOKEN}", "Content-Type": "application/json"}
    list_url = f"https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records"
    response = requests.get(list_url, headers=headers, params={"type": "TXT", "name": DOMAIN})
    response.raise_for_status()
    
    existing = next((r for r in response.json()["result"] if r["content"].startswith("v=spf1")), None)
    if existing:
        update_url = f"{list_url}/{existing['id']}"
        payload = {"type": "TXT", "name": DOMAIN, "content": record, "ttl": 3600}
        r = requests.put(update_url, headers=headers, json=payload)
        r.raise_for_status()
        print(f"Updated SPF record: {record}")

def alert(message: str) -> None:
    """Send Slack alert via webhook."""
    requests.post(ALERT_WEBHOOK, json={"text": f"🚨 SPF flatten: {message}"})

def main():
    all_ips = []
    for include in INCLUDES_TO_FLATTEN:
        all_ips.extend(resolve_spf_ips(include))
    
    flattened = build_flattened_record(all_ips, ADDITIONAL_IPS)
    
    if len(flattened) > 450:
        alert(f"SPF record over 450 chars ({len(flattened)}) — DNS TXT may need split")
        sys.exit(1)
    
    update_cloudflare_dns(flattened)
    print(f"Success: {len([t for t in flattened.split() if t.startswith('ip')])} IPs deduplicated")

if __name__ == "__main__":
    main()

Schedule with cron at daily cadence:

# /etc/cron.d/spf-flatten
0 3 * * * www-data /usr/local/bin/spf-flatten.py >> /var/log/spf-flatten.log 2>&1

The setup time is 16-32 engineer hours including testing. The ongoing maintenance is approximately 4-8 hours per month: reviewing logs, handling alert fires, updating the include list when adding new vendors, and occasionally debugging when a vendor’s SPF returns unexpected content.

How to measure your current lookup count

Before choosing a fix method, you must know your current state. Three free tools that count SPF lookups precisely:

  1. MXToolbox SPF Surveyor (mxtoolbox.com/spf.aspx) — the most widely used. Shows the resolution tree visually plus the lookup count.

  2. Dmarcian SPF Surveyor (dmarcian.com/spf-survey/) — free, similar functionality, sometimes better at handling edge cases like deeply-nested includes.

  3. Command-line check via dig:

    dig +short TXT yourdomain.com | grep "v=spf1"
    # Then manually count: each include:, a, mx, redirect=, exists= is 1 lookup
    # Each nested include adds its own count

Use one of the GUI tools first — they’re more accurate and faster. The CLI is useful for scripting in monitoring systems but laborious for one-off checks.

Identifying your sending sources — the step most senders skip

Before fixing SPF, you need a complete list of who is sending email on behalf of your domain. The pattern we see consistently in audits: 30-50% of operators don’t actually know all their sending sources. Common forgotten sources:

  • Marketing tools onboarded years ago and never decommissioned
  • HR systems sending performance review notifications
  • Procurement platforms sending vendor requests
  • CRM integrations sending campaigns sales reps don’t realize go through the corporate domain
  • Calendar invitation services (Calendly, Doodle, etc.)
  • Survey tools (Typeform, SurveyMonkey)
  • Status page services (Statuspage.io, Better Uptime)
  • Project management notifications (Asana, Jira, Monday.com)
  • Payment processors (Stripe receipts, PayPal notifications)

The discovery process:

  1. Pull DMARC RUA reports for the past 90 days and identify all source IPs / sending domains. Tools like dmarcian, EasyDMARC, Postmark DMARC Monitoring (free) parse this for you.

  2. Survey internal teams: Marketing, Sales, Customer Success, HR, Finance, IT — ask each what tools they use that might send email. Don’t trust the tribal knowledge of any single team.

  3. Audit corporate credit card statements for SaaS subscriptions that might include email sending (this catches “shadow IT” services).

  4. Cross-reference with active services in your SPF: anything in the SPF that no team admits to using is a candidate for pruning.

The output of this step is a definitive list of sending services that justifies the SPF architecture going forward. Without it, you’re fixing a moving target.

SPF macros — the “advanced” option that rarely applies

RFC 7208 includes macros (%{i}, %{s}, etc.) that allow dynamic SPF resolution. They are sometimes presented as a fix for the lookup limit, but in practice macros rarely apply because:

  1. Most receivers (including Gmail) treat macro-based SPF as suspicious and may downgrade authentication confidence.
  2. Macros increase complexity dramatically without solving the fundamental scaling problem.
  3. Debugging macro-based SPF is significantly harder than debugging static SPF.

The realistic use case for macros is per-tenant SPF in multi-tenant ESPs (where each customer needs their own authorization scope). Outside of that specific case, macros are not the answer.

Common fix mistakes

Mistake 1 — Flattening without monitoring. If you flatten manually and the vendor changes IPs, your flattened record is now incorrect. Mail still passes SPF for the old IPs but fails for the new ones. This is the #1 cause of “the fix worked, then 3 months later mail started failing again.”

Mistake 2 — Setting +all or ?all to bypass the issue. We’ve seen senders try to “skip” SPF validation by setting +all (pass everything). This is catastrophically bad: it tells receivers your SPF should pass for any sender, which is a green light for spammers spoofing your domain. Always use -all or ~all.

Mistake 3 — Not propagating the new SPF before vendor change. The change to vendor Return-Path or DNS must propagate before the next mail batch. Allow 24-48 hours for DNS TTL propagation; otherwise the first batches after the change fail authentication.

Mistake 4 — Forgetting the apex SPF after subdomain delegation. The parent domain still needs an SPF record (e.g., v=spf1 -all for “no mail from apex”) because some receivers check it. Don’t leave the apex without an SPF record after delegating.

Mistake 5 — Mixing flattening with includes. We’ve seen records like v=spf1 ip4:1.2.3.4/24 include:vendor.com -all where the operator partially flattened. This rarely works because the include still consumes lookups, defeating the purpose. Choose one approach per record.

30-day migration runbook — the complete fix

For senders with active PermError who need to fix it correctly without rushing into the wrong solution:

Day 1-2 — Discovery

  • Measure current lookup count via MXToolbox + Dmarcian
  • Pull last 30 days of DMARC RUA reports
  • Document all current sending services with vendor + use case + Return-Path domain

Day 3-5 — Strategy decision

  • Use the decision tool above to identify fix method
  • Document chosen strategy with rationale (CYA documentation if anyone questions later)
  • Identify vendor coordination required for the chosen method

Day 6-10 — Vendor coordination

  • Contact each vendor’s support to configure custom Return-Path (subdomain delegation) OR
  • Build flattening automation in test environment

Day 11-14 — Test environment validation

  • Configure subdomain SPF records OR flattened record in DNS
  • Send test emails through each service
  • Verify Authentication-Results headers show spf=pass
  • Verify DMARC alignment shows pass

Day 15-18 — Production rollout (gradual)

  • Update one service at a time, watch DMARC reports for 24 hours after each
  • If failures appear in DMARC RUA for the migrated service, roll back and investigate
  • Continue until all services migrated

Day 19-25 — Old SPF deprecation

  • Update the apex SPF to remove the includes that are now on subdomains
  • Verify old apex SPF lookup count drops below 10
  • Wait 48 hours for DNS propagation

Day 26-30 — Monitoring setup

  • Configure monthly recurring lookup count check (cron job + alert)
  • Set up DMARC report parsing (parsedmarc, dmarcian, Postmark DMARC Monitoring)
  • Document architecture for next operator

This 30-day plan reduces risk because every migration step is reversible within 24 hours by restoring the previous DNS record. Operators who try to do this in 2 days have a higher failure rate.

Post-fix monitoring and alerts — what to set up

After fixing SPF, prevent regression with proactive monitoring:

Monthly automated lookup count check via cron + a script:

#!/bin/bash
# /usr/local/bin/spf-lookup-check.sh
DOMAIN="yourdomain.com"
LOOKUPS=$(curl -s "https://api.dmarcian.com/v2/spf?domain=$DOMAIN" | jq -r '.lookup_count')
THRESHOLD=9
if [ "$LOOKUPS" -gt "$THRESHOLD" ]; then
  curl -X POST $SLACK_WEBHOOK -d "{\"text\":\"⚠ SPF lookup count for $DOMAIN: $LOOKUPS (threshold $THRESHOLD)\"}"
fi

DMARC RUA parsing is essential for catching SPF failures that wouldn’t show in lookup counts:

  • Self-hosted: parsedmarc + Elasticsearch + Grafana (covered in our DMARC self-hosted post)
  • Managed: dmarcian, EasyDMARC, Postmark DMARC Monitoring (free tier works for under 1M/month)

Quarterly architectural review:

  • Has the team added new sending services? Are they on the right subdomain?
  • Have any vendors changed their SPF records in ways that affect counts?
  • Is the documentation still accurate?

Annual deep audit: full review of SPF architecture against current sending patterns; consolidation opportunities; deprecation of unused includes.

ESP-specific analysis — how many lookups each provider consumes

Different sending services consume vastly different lookup budgets. The current state in 2026:

ServiceMechanismLookups consumedNotes
Google Workspaceinclude:_spf.google.com4Nested for sub-services (Gmail, Calendar, etc.)
Microsoft 365include:spf.protection.outlook.com3Stable, well-maintained
Mailguninclude:mailgun.org2Region-specific subdomains exist
SendGridinclude:sendgrid.net3Includes nested for data centers
Postmarkinclude:spf.mtasv.net2Minimal, well-engineered
Amazon SESinclude:amazonses.com1Surprisingly compact
Mailchimpinclude:servers.mcsv.net2Moderate
HubSpotinclude:_spfa.hubspot.com2Plus separate transactional include
Salesforceinclude:_spf.salesforce.com4High due to multi-cloud architecture
Zendeskinclude:mail.zendesk.com2Stable
Intercominclude:_spf.intercom.io2Moderate
Stripeinclude:_spf.stripe.com2Transactional, stable
Apolloinclude:_spf.apollo.io2Newer service, stable
Lemlistinclude:_spf.lemlist.com2Cold outreach

Pattern: Google + Salesforce alone consume 8 lookups, leaving only 2 lookups for everything else. This is why operators with both providers hit the limit so quickly. The math works against you fast.

When SPF flattening is NOT the right answer

Three scenarios where flattening is structurally wrong:

  1. You have only 1-3 services and never plan to grow. Pruning + monitoring is sufficient. Flattening is over-engineering.

  2. Your vendors actively rotate IPs (rare but exists). Some smaller ESPs change IPs unpredictably without TTL coordination. Flattening that fast-changing IP set is operationally infeasible. Subdomain delegation works better here.

  3. Your DNS provider has API rate limits that conflict with cron-based flattening. Some smaller DNS providers (especially registrar-bundled DNS) limit API calls to a level incompatible with daily flattening updates. Either migrate DNS to Cloudflare/Route53 or use subdomain delegation.

Continuous best practices — governance preventing future SPF crises

Three governance practices that prevent the SPF lookup limit from being hit again:

Practice 1: Service onboarding checklist. Whenever the company adds a new vendor that sends email, an explicit step in onboarding asks: “Does this vendor send email on behalf of our domain? If yes, what subdomain Return-Path should it use?” Embedding this question into vendor onboarding prevents accidental include accumulation.

Practice 2: Annual SPF audit. Once per year, formally audit the SPF architecture against the actual sending pattern visible in DMARC RUA reports. Decommission services that no longer appear in the data. This is a 2-4 hour exercise that prevents creep.

Practice 3: Stakeholder education. Train the marketing, sales, and IT teams that “we have a 10-lookup SPF budget.” When they understand the constraint, they self-regulate adding new services. Without this education, the SPF accumulates includes silently.

What we run at Blue Spirit

For transparency: we operate dedicated PowerMTA infrastructure (PowerMTA hosting) where Return-Path is by default on a subdomain customer-controlled, eliminating SPF lookup pressure for our hosted senders. We use subdomain delegation extensively in our own corporate sending and recommend it as the first-line fix for clients except in specific cases where DIY flattening or managed services are structurally better.

The honest summary of SPF fix in 2026: subdomain delegation is the right answer for 80% of senders with DevOps capacity; managed services fit the 15% without DevOps capacity; DIY flattening is appropriate for the 5% with very specific operational requirements; pruning is triage, not a long-term solution. The right method depends on your operational profile, infrastructure stability, and budget reality — not on which service is better marketed in search results.

If you want help diagnosing your current SPF state and choosing the right fix path — or executing the migration from a broken state — that’s part of our deliverability audit. We see SPF lookup limit issues in the majority of audits we conduct, and the fix is rarely as expensive as managed-service marketing suggests.

SPF is one layer of the authentication stack. For the complete picture see our email authentication 2026 guide covering SPF, DKIM, DMARC, MTA-STS, and BIMI. DKIM alignment that complements SPF is detailed in DKIM key rotation on PowerMTA + OpenDKIM. For the DMARC enforcement progression that depends on SPF alignment, the DMARC enforcement 2026 survival guide covers p=none → p=quarantine → p=reject. For DMARC aggregate report parsing to verify SPF alignment in production see DMARC aggregate self-hosted with parsedmarc and Elasticsearch. For MTA-STS that complements SPF on the transport layer see MTA-STS and TLS-RPT implementation guide.


Need help fixing SPF without paying $300+/month for a managed service? That’s part of our deliverability audit. We diagnose your specific SPF state and recommend the fix method that fits your operational reality, not the one that pays vendor commissions.

Femke van der Berg

Senior Deliverability Engineer · Email Infrastructure

Something we should write about? Reply with your topic at [email protected].

Chat with us on WhatsApp