Skip to content

Email Security & Deliverability

Email is the most widely used communication protocol on the internet and simultaneously one of the least secure. SMTP was designed in 1982 (RFC 821) with zero authentication — any server could claim to send mail from any domain. Four decades later, the entire email authentication ecosystem (SPF, DKIM, DMARC, BIMI, MTA-STS, ARC) exists as a series of patches on top of a fundamentally trusting protocol. Understanding this stack is not optional for any engineer who sends transactional emails, manages DNS records, or needs to keep phishing emails out of their organization.

Related: Encryption in Transit | API Security | DNS & Networking | Supply Chain Security


Key Takeaway

  • Email has no built-in sender authentication — SPF, DKIM, and DMARC are retrofitted layers that together form the authentication chain, and you need all three
  • SPF validates the sending IP, DKIM validates the message integrity via cryptographic signatures, and DMARC ties them together with policy enforcement and reporting
  • A DMARC policy of p=none is monitoring mode only — it does not block anything; you must progress through quarantine to reject to actually protect your domain
  • Email deliverability is a reputation game — authentication is table stakes, but sender reputation, engagement metrics, and infrastructure hygiene determine whether your emails reach the inbox or the spam folder
  • Every domain you own (including parked domains that never send email) needs a DMARC record to prevent abuse

Common Misconceptions

  • "SPF alone prevents spoofing." SPF only checks the envelope MAIL FROM address, not the From: header that users see. An attacker can pass SPF while showing a spoofed From: header. You need DMARC alignment to close this gap.
  • "DKIM guarantees the email came from the domain owner." DKIM proves the email was signed by someone with access to the private key. If the key is compromised, or if a third-party service signs on your behalf, DKIM alone does not prove sender identity.
  • "DMARC reject means no one can spoof our domain." DMARC only works at receiving servers that check it. Some legacy systems, small ISPs, or misconfigured servers may not enforce DMARC at all. It drastically reduces spoofing but cannot eliminate it entirely.
  • "We don't send email from this domain, so we don't need email security records." Domains that don't send email are prime targets for spoofing. You absolutely need v=spf1 -all, a DKIM record that publishes no key, and v=DMARC1; p=reject on every domain you own.
  • "Moving to a new ESP is just a DNS change." Changing email service providers requires updating SPF includes, rotating DKIM keys, re-warming the new IP range, and monitoring DMARC reports for alignment failures during transition. A careless migration can tank deliverability for weeks.
  • "High open rates mean good deliverability." Open rate tracking relies on pixel loading, which is increasingly blocked by Apple Mail Privacy Protection (since iOS 15) and corporate proxies. Open rates are an unreliable deliverability signal.

One-Liner Summary

Email authentication is a three-layer retrofit (SPF checks the sending server, DKIM checks the message signature, DMARC enforces alignment between them) that compensates for SMTP having been designed with zero sender verification.


Email Authentication Chain

How Email Actually Works

To understand email security, you need to understand the SMTP flow and the critical distinction between the envelope and the header.

Key SMTP commands in the flow:

SMTP CommandPurposeSecurity Relevance
EHLO sender.comIdentify the sending serverCan be faked — no verification
MAIL FROM:<alice@sender.com>Envelope sender (return path)SPF checks this address
RCPT TO:<bob@recipient.com>Envelope recipientDetermines routing
DATAMessage headers + body followDKIM signs this content

Envelope vs Header From — The Root of Spoofing

This is the single most important concept in email security. There are two separate "From" addresses in every email:

SMTP Envelope (invisible to user):
  MAIL FROM:<notifications@esp.sendgrid.net>    ← SPF checks this

Message Headers (visible to user):
  From: "Your Bank" <security@yourbank.com>     ← What the user SEES
  Return-Path: <bounce-123@esp.sendgrid.net>    ← Derived from envelope

The user sees the From: header. SPF only validates the envelope MAIL FROM. Without DMARC alignment, an attacker can pass SPF with their own domain in the envelope while showing your domain in the From: header. This is exactly what phishing exploits.

Why Email Is Inherently Insecure

The original SMTP specification (RFC 821, 1982) assumed a small network of trusted academic institutions. It provided:

  • No sender authentication — any server can claim any identity
  • No encryption — messages travel in plaintext (STARTTLS was added in 1999)
  • No integrity checking — messages can be modified in transit
  • No delivery confirmation — you cannot prove delivery or non-delivery

Every email security mechanism we use today is a retrofit:

YearStandardWhat It Fixes
1999STARTTLS (RFC 2487)Encryption in transit (opportunistic)
2006SPF (RFC 4408)Sender IP authorization
2007DKIM (RFC 4871)Message integrity and origin
2012DMARC (RFC 7489)Policy enforcement tying SPF + DKIM together
2018MTA-STS (RFC 8461)Mandatory TLS enforcement
2019ARC (RFC 8617)Authentication preservation through forwarding
2021BIMI (RFC pending)Visual brand verification in inboxes

The Authentication Trinity

SPF, DKIM, and DMARC work together as a chain. Each solves a specific problem, and none is sufficient alone:

DMARC passes if either SPF or DKIM passes and is aligned with the From: header domain. This is an OR relationship — you do not need both to pass, but having both provides redundancy.


SPF (Sender Policy Framework)

SPF lets a domain owner publish a DNS TXT record listing which IP addresses and servers are authorized to send email on behalf of that domain. The receiving server checks the envelope MAIL FROM domain's SPF record and verifies the connecting IP is on the list.

How SPF Works

SPF Syntax Deep Dive

An SPF record is a DNS TXT record on the domain's root that starts with v=spf1:

dns
v=spf1 ip4:198.51.100.0/24 ip6:2001:db8::/32 include:_spf.google.com ~all

Mechanisms (what to check):

MechanismDescriptionExample
ip4Match an IPv4 address or CIDR rangeip4:203.0.113.5 or ip4:203.0.113.0/24
ip6Match an IPv6 address or prefixip6:2001:db8::/32
includeRecursively check another domain's SPFinclude:_spf.google.com
aMatch the domain's A/AAAA record IPsa or a:mail.example.com
mxMatch the domain's MX record IPsmx or mx:example.com
existsPass if a DNS A record existsexists:%{i}._spf.example.com (macro)
allMatch everything (used as default)-all (always last)
redirectUse another domain's SPF insteadredirect=_spf.example.com

Qualifiers (what the result means):

QualifierPrefixResult if MatchedRecommended Usage
Pass+ (default)AcceptAuthorized senders
Fail-RejectStrict all terminator
SoftFail~Accept but markTransitional all terminator
Neutral?No opinionTesting only

SPF Alignment

SPF alignment means the domain in the envelope MAIL FROM matches the domain in the From: header. DMARC requires this alignment for SPF to count.

Relaxed alignment (default): The organizational domain must match. bounce@mail.example.com aligns with From: ceo@example.com because both share example.com.

Strict alignment: The exact FQDN must match. bounce@mail.example.com does NOT align with From: ceo@example.com.

Common SPF Mistakes

1. Too many DNS lookups (the 10-lookup limit)

SPF is limited to 10 DNS lookups (mechanisms that trigger lookups: include, a, mx, exists, redirect). ip4 and ip6 do not count. Exceeding 10 lookups causes a permerror, and many receivers treat this as a fail.

dns
# BAD — 12 lookups (will fail)
v=spf1 include:_spf.google.com include:spf.protection.outlook.com
       include:sendgrid.net include:spf.mandrillapp.com
       include:mail.zendesk.com include:_spf.salesforce.com
       include:servers.mcsv.net include:spf.freshdesk.com
       a mx -all

# Each include can itself contain nested includes, compounding the problem

2. Flattening SPF records

To work around the 10-lookup limit, some admins "flatten" includes into raw IP addresses:

dns
# Flattened (fragile — breaks when provider changes IPs)
v=spf1 ip4:209.85.128.0/17 ip4:216.239.32.0/19 ip4:64.233.160.0/19
       ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18
       ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16
       ip4:207.126.144.0/20 -all

This works until Google changes their IP ranges (which they do regularly). Use an SPF flattening service that auto-updates, or restructure to reduce includes.

3. Multiple SPF records

A domain must have exactly ONE SPF TXT record. Multiple records cause permerror:

dns
# BAD — two SPF records
example.com TXT "v=spf1 include:_spf.google.com -all"
example.com TXT "v=spf1 include:sendgrid.net -all"

# GOOD — combined into one
example.com TXT "v=spf1 include:_spf.google.com include:sendgrid.net -all"

SPF Records for Common Providers

dns
# Google Workspace
v=spf1 include:_spf.google.com ~all

# Microsoft 365
v=spf1 include:spf.protection.outlook.com ~all

# Google Workspace + SendGrid
v=spf1 include:_spf.google.com include:sendgrid.net ~all

# Microsoft 365 + Amazon SES (us-east-1)
v=spf1 include:spf.protection.outlook.com include:amazonses.com ~all

# Minimal — domain sends no email
v=spf1 -all

In Production

SPF lookups count recursively. Before adding a new include, check how many lookups it costs. Google's _spf.google.com alone uses 4 lookups (it includes _netblocks.google.com, _netblocks2.google.com, and _netblocks3.google.com). Use dig TXT _spf.google.com or MXToolbox to trace the full chain. Many teams hit the 10-lookup limit the moment they add their third ESP and wonder why emails start failing silently.


DKIM (DomainKeys Identified Mail)

DKIM adds a cryptographic signature to outgoing emails. The sending server signs specific headers and the body with a private key, and the receiving server verifies the signature using the public key published in DNS.

How DKIM Works

A DKIM signature header looks like this:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
    d=example.com; s=google;
    h=from:to:subject:date:message-id:mime-version;
    bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
    b=AuUoFEfDxTDkHlLXSZEpZj79LICEps6eda7W3deTVFOk2PTrwoY0qG
      JGK2Z4e0pNLv1MfRCh34EFJ1ztXxUJiXZVoH...
TagPurpose
v=1DKIM version
a=rsa-sha256Signing algorithm (RSA with SHA-256 hash)
c=relaxed/relaxedCanonicalization (header/body)
d=example.comSigning domain (used for DMARC alignment)
s=googleSelector (identifies which key to use)
h=from:to:subject:...Signed headers
bh=...Body hash (Base64)
b=...Signature (Base64)

DKIM Selector and DNS Record

The selector allows a domain to publish multiple DKIM keys (for different services or key rotation). The DNS lookup is:

<selector>._domainkey.<domain>

For example, if the selector is google and the domain is example.com:

dns
google._domainkey.example.com TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
TagValuesDescription
v=DKIM1RequiredVersion
k=rsarsa, ed25519Key algorithm
p=...Base64 public keyThe verification key
t=sOptionalStrict mode — domain in d= must exactly match the From: domain
t=yOptionalTesting mode — do not enforce failures

Key Rotation Strategies

DKIM private keys should be rotated periodically (every 6-12 months) to limit exposure if a key is compromised.

Rotation with zero downtime:

  1. Publish the new public key in DNS under a new selector
  2. Wait for DNS propagation (24-48 hours)
  3. Switch the signing server to use the new private key and selector
  4. Keep the old public key in DNS for at least 7 days (emails in transit may still carry the old signature)
  5. Remove the old public key from DNS

In Production

Use date-based selectors like dkim202604 or s202604 to make rotation tracking trivial. When you see a DKIM failure in DMARC reports, you can immediately tell whether it is from a stale signature signed with a rotated-out key. Many teams use opaque selectors like s1 and s2 and lose track of which is current within months.

DKIM Canonicalization

Before hashing, both headers and body are canonicalized (normalized) to handle minor modifications by mail servers in transit.

ModeHeadersBody
SimpleNo changes — exact byte match requiredIgnore trailing empty lines only
RelaxedLowercase header names, collapse whitespaceCollapse whitespace, ignore trailing empty lines

Best practice: Use c=relaxed/relaxed. Mailing lists and forwarding services frequently modify whitespace and header casing. Simple canonicalization causes unnecessary DKIM failures.

Third-Party Sender DKIM Setup

When using an ESP (SendGrid, Postmark, etc.), you have two DKIM signing options:

Option 1: ESP signs with their domain (default)

DKIM-Signature: d=sendgrid.net; s=s1; ...

This passes DKIM but fails DMARC alignment (the d= domain does not match your From: domain).

Option 2: ESP signs with YOUR domain (recommended)

DKIM-Signature: d=example.com; s=sg1; ...

You publish the ESP-provided DKIM public key in your DNS:

dns
sg1._domainkey.example.com CNAME sg1.domainkey.sendgrid.net.

Always configure custom DKIM signing for every third-party service that sends email as your domain.


DMARC (Domain-based Message Authentication, Reporting & Conformance)

DMARC is the policy layer. It answers two questions: (1) What should a receiver do when SPF and DKIM both fail alignment? (2) Where should authentication results be reported?

How DMARC Ties SPF and DKIM Together

DMARC requires at least one of SPF or DKIM to both pass and align with the From: header domain. The alignment check is what closes the envelope-vs-header spoofing gap that SPF alone cannot address.

DMARC Record Syntax

DMARC records are published as a TXT record at _dmarc.<domain>:

dns
_dmarc.example.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc-agg@example.com; ruf=mailto:dmarc-forensic@example.com; adkim=r; aspf=r; pct=100; sp=reject"
TagValuesDescription
v=DMARC1RequiredVersion identifier
pnone, quarantine, rejectPolicy for failing messages
spnone, quarantine, rejectSubdomain policy (defaults to p if absent)
ruamailto:...Aggregate report destination
rufmailto:...Forensic (failure) report destination
adkimr (relaxed), s (strict)DKIM alignment mode
aspfr (relaxed), s (strict)SPF alignment mode
pct1-100Percentage of failing mail to apply policy
riSecondsReporting interval (default 86400 = 24h)
fo0, 1, d, sForensic report options

DMARC Policies — Deployment Progression

Never go straight to p=reject. The deployment path is:

Phase 1: p=none (monitoring) — Collect reports for 2-4 weeks. Identify all legitimate senders. Fix SPF/DKIM for each. This phase reveals shadow IT services sending email as your domain that you did not know about.

Phase 2: p=quarantine with pct ramp — Start quarantining a small percentage of failing mail. Increase gradually while monitoring for false positives (legitimate mail failing DMARC).

Phase 3: p=reject — Failing messages are rejected outright. This is the goal for every domain.

DMARC Alignment: Relaxed vs Strict

AlignmentSPF CheckDKIM Check
Relaxed (aspf=r, adkim=r)Envelope MAIL FROM org domain matches From: header org domainDKIM d= org domain matches From: header org domain
Strict (aspf=s, adkim=s)Exact FQDN match requiredExact FQDN match required

Example with relaxed alignment:

  • From: user@example.com + envelope MAIL FROM: bounce@mail.example.com = SPF aligned (both share example.com)
  • From: user@example.com + DKIM d=mail.example.com = DKIM aligned (both share example.com)

Example with strict alignment:

  • From: user@example.com + envelope MAIL FROM: bounce@mail.example.com = SPF NOT aligned (different FQDNs)

Start with relaxed alignment. Move to strict only after confirming all senders use matching domains.

DMARC Reporting

DMARC aggregate reports (rua) are XML files sent daily by receiving mail servers. They tell you:

  • Which IPs sent email claiming to be your domain
  • Whether each message passed or failed SPF, DKIM, and DMARC
  • What policy was applied

Sample aggregate report structure (simplified):

xml
<?xml version="1.0" encoding="UTF-8"?>
<feedback>
  <report_metadata>
    <org_name>google.com</org_name>
    <date_range>
      <begin>1711929600</begin>
      <end>1712016000</end>
    </date_range>
  </report_metadata>
  <policy_published>
    <domain>example.com</domain>
    <adkim>r</adkim>
    <aspf>r</aspf>
    <p>reject</p>
    <pct>100</pct>
  </policy_published>
  <record>
    <row>
      <source_ip>198.51.100.42</source_ip>
      <count>1523</count>
      <policy_evaluated>
        <disposition>none</disposition>
        <dkim>pass</dkim>
        <spf>pass</spf>
      </policy_evaluated>
    </row>
  </record>
  <record>
    <row>
      <source_ip>203.0.113.99</source_ip>
      <count>47</count>
      <policy_evaluated>
        <disposition>reject</disposition>
        <dkim>fail</dkim>
        <spf>fail</spf>
      </policy_evaluated>
    </row>
  </record>
  <identifiers>
    <header_from>example.com</header_from>
  </identifiers>
  <auth_results>
    <dkim>
      <domain>example.com</domain>
      <result>pass</result>
      <selector>dkim202604</selector>
    </dkim>
    <spf>
      <domain>example.com</domain>
      <result>pass</result>
    </spf>
  </auth_results>
</feedback>

Reading the report:

  • 198.51.100.42 sent 1,523 emails — both DKIM and SPF passed. This is likely your legitimate sender.
  • 203.0.113.99 sent 47 emails — both failed. This is likely a spoofer, and the messages were rejected.

Forensic reports (ruf) contain individual failure details (headers of failing messages). They are less commonly sent because of privacy concerns — many large providers (Google, Microsoft) do not send ruf reports.

Subdomain Policies

The sp tag controls what happens to mail from subdomains. Without sp, subdomains inherit the parent's p policy.

dns
# Parent domain rejects; subdomains quarantine (maybe marketing uses a subdomain)
_dmarc.example.com TXT "v=DMARC1; p=reject; sp=quarantine; rua=mailto:dmarc@example.com"

# Lock down all subdomains that shouldn't send email
_dmarc.example.com TXT "v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc@example.com"

You can also publish per-subdomain DMARC records:

dns
_dmarc.marketing.example.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

Advanced Email Security

BIMI (Brand Indicators for Message Identification)

BIMI lets organizations display their logo next to emails in supporting inboxes (Gmail, Apple Mail, Yahoo, Fastmail). It requires DMARC at p=quarantine or p=reject as a prerequisite.

dns
default._bimi.example.com TXT "v=BIMI1; l=https://example.com/brand/logo.svg; a=https://example.com/brand/vmc.pem"
TagDescription
lURL to your SVG logo (must be Tiny PS profile SVG)
aURL to your VMC (Verified Mark Certificate) from a CA like DigiCert or Entrust

Requirements for BIMI:

  1. DMARC policy of quarantine or reject at pct=100
  2. SVG Tiny PS logo (specific SVG profile required)
  3. VMC certificate from an authorized Certificate Authority (costs ~$1,000-1,500/year)
  4. Trademark registration for the logo (VMC requirement)

BIMI is a marketing investment, not a security necessity. But it incentivizes proper DMARC deployment because the logo only appears when authentication passes.

MTA-STS (SMTP MTA Strict Transport Security)

STARTTLS is opportunistic — a man-in-the-middle can strip the TLS negotiation and force a plaintext connection (STRIPTLS attack). MTA-STS forces sending servers to use TLS when delivering to your domain.

Setup requires two components:

  1. DNS TXT record:
dns
_mta-sts.example.com TXT "v=STSv1; id=20260404"
  1. Policy file at https://mta-sts.example.com/.well-known/mta-sts.txt:
version: STSv1
mode: enforce
mx: mail.example.com
mx: backup-mail.example.com
max_age: 604800
ModeBehavior
testingReport failures via TLSRPT but still deliver
enforceRefuse to deliver if TLS cannot be established

TLSRPT (TLS Reporting) works alongside MTA-STS:

dns
_smtp._tls.example.com TXT "v=TLSRPTv1; rua=mailto:tlsrpt@example.com"

DANE/TLSA Records

DANE (DNS-Based Authentication of Named Entities) uses DNSSEC to publish the expected TLS certificate in DNS, preventing CA compromise and MITM attacks:

dns
_25._tcp.mail.example.com TLSA 3 1 1 e3b0c44298fc1c149afbf4c8996fb924...
FieldValueMeaning
Usage3DANE-EE (end entity, trust anchor is the certificate itself)
Selector1Match the SubjectPublicKeyInfo
Matching1SHA-256 hash of the selected content

DANE requires DNSSEC on your domain. Without DNSSEC, DANE records are ignored. This is the primary barrier to adoption.

ARC (Authenticated Received Chain)

When emails are forwarded through mailing lists, the forwarding server changes the envelope sender (breaking SPF) and may modify headers or body (breaking DKIM). ARC preserves the original authentication results through a chain of custody.

ARC adds three headers at each hop:

HeaderPurpose
ARC-Authentication-ResultsSnapshot of auth results at this hop
ARC-Message-SignatureDKIM-like signature of the message at this hop
ARC-SealSignature over all previous ARC headers (chain integrity)

ARC is evaluated by the final receiver. If the receiver trusts the forwarding intermediary, it can use the ARC chain to recover the original authentication results.


Email Deliverability

Authentication (SPF, DKIM, DMARC) is necessary but not sufficient for inbox placement. Deliverability is determined by a combination of authentication, reputation, content, and recipient engagement.

Sender Reputation

Mailbox providers maintain reputation scores for both IP addresses and domains:

IP reputation factors:

  • Volume and consistency of sending
  • Bounce rates (high bounces = bad list hygiene)
  • Spam complaint rates (FBL data)
  • Presence on blocklists (Spamhaus, Barracuda, SORBS)
  • Age of the IP (new IPs have no reputation)

Domain reputation factors:

  • DMARC policy and compliance rate
  • Historical spam complaint rate
  • Engagement metrics (opens, clicks, replies)
  • Domain age and DNS configuration quality

Google Postmaster Tools (free) shows your domain and IP reputation for Gmail delivery. Microsoft SNDS provides similar data for Outlook.

Warm-Up Strategies for New IPs/Domains

A new IP or domain has no sending history. Sending a large volume immediately triggers spam filters. Warm-up builds reputation gradually:

DayDaily VolumeNotes
1-350-100Send to your most engaged recipients only
4-7200-500Monitor bounce rates (must stay below 2%)
8-14500-2,000Monitor spam complaints (must stay below 0.1%)
15-212,000-10,000Check Google Postmaster Tools for reputation
22-3010,000-50,000Gradually increase to target volume
30+Full volumeMaintain consistent daily volume

Critical warm-up rules:

  • Start with recipients who have recently engaged (opened, clicked)
  • Avoid purchased lists or unverified addresses during warm-up
  • Maintain consistent volume — sending 100 emails one day and 50,000 the next destroys reputation
  • Monitor blocklists daily during warm-up (MXToolbox, MultiRBL)

Bounce Handling

TypeDescriptionAction
Hard bouncePermanent failure (address doesn't exist, domain doesn't exist)Remove immediately — never retry
Soft bounceTemporary failure (mailbox full, server down, rate limited)Retry 2-3 times over 24-72 hours, then suppress
Block bounceRejected by policy (blocklisted, content filtered)Investigate the reason; do not blindly retry

Hard bounce rate thresholds:

  • Below 2%: Acceptable
  • 2-5%: Warning — clean your list
  • Above 5%: Critical — ESPs may suspend your account

Spam Score Factors

Modern spam filters use hundreds of signals. The major categories:

CategoryWeightExamples
AuthenticationHighSPF/DKIM/DMARC pass/fail
Sender reputationHighIP and domain history
ContentMediumSpammy phrases, ALL CAPS, excessive images, URL shorteners
TechnicalMediumMissing unsubscribe header, invalid HTML, oversized messages
EngagementHigh (Gmail)Opens, clicks, replies, "mark as spam" rate
List qualityHighBounce rate, complaint rate, spam trap hits

Content red flags:

  • "FREE", "ACT NOW", "LIMITED TIME" in subject lines
  • Image-only emails with no text
  • URL shorteners (bit.ly, etc.) in email body
  • Mismatched display URLs (Click here linking to evil.com)
  • Missing List-Unsubscribe header

Feedback Loops (FBLs)

FBLs let you receive notifications when recipients mark your email as spam. Most major providers offer FBLs:

ProviderFBL ProgramFormat
MicrosoftJMRP (Junk Mail Reporting Program)ARF
YahooCFL (Complaint Feedback Loop)ARF
AOLFBLARF
GmailNo traditional FBL — use Postmaster ToolsDashboard only

When you receive an FBL complaint, immediately suppress that address from future sends. A complaint rate above 0.1% is dangerous; above 0.3% and you will likely be blocklisted.

Dedicated vs Shared IPs

FactorShared IPDedicated IP
Volume requirementAny volume50,000+ emails/month minimum
Reputation controlShared with other senders (risk of neighbor pollution)Fully yours
Warm-up neededNo (already warmed)Yes (must build from scratch)
CostIncluded in ESP planAdditional fee ($20-80/month)
Best forLow-volume senders, startupsHigh-volume, reputation-sensitive senders

In Production

Most startups should stay on shared IPs. A dedicated IP that sends too little volume (under 50K/month) actually hurts deliverability because mailbox providers do not have enough data to build a positive reputation. You end up in a worse position than sharing an IP pool with other vetted senders. Only move to dedicated when your volume justifies it and you have the operational discipline to maintain consistent sending patterns.


Transactional Email Services

Provider Comparison

FeatureSendGridPostmarkAmazon SESMailgunResend
Pricing modelTiered plans + overagesPer-message ($1.25/1K)Pay-per-use ($0.10/1K)Tiered plansPer-message ($3/1K free tier)
Free tier100/day (forever)100/month62K/month (on EC2)100/day (trial)3K/month
SMTP relayYesYesYesYesYes
REST APIYesYesYesYesYes (modern)
Dedicated IPsFrom Pro planIncluded on higher plans$24.95/month per IPFrom Scale planComing soon
DKIM customYes (CNAME)Yes (CNAME)Yes (CNAME)Yes (TXT)Yes (CNAME)
Transactional focusMixed (also marketing)Transactional onlyRaw infrastructureMixedDeveloper-first
Webhook eventsDelivered, opened, clicked, bounced, spam reportDelivered, bounced, spam complaint, opened, clickedBounce, complaint, deliveryAll standard eventsAll standard events
Best forGeneral purpose, high volumeMission-critical transactional (highest deliverability)Cost-sensitive, AWS-nativeDevelopers, email parsingModern DX, React Email

Integration Patterns

SMTP Relay — Configure your application's SMTP settings to route through the ESP:

python
# Python (smtplib via SendGrid SMTP relay)
import smtplib
from email.mime.text import MIMEText

msg = MIMEText("Your order #1234 has shipped.")
msg["Subject"] = "Order Shipped"
msg["From"] = "orders@example.com"
msg["To"] = "customer@gmail.com"

with smtplib.SMTP("smtp.sendgrid.net", 587) as server:
    server.starttls()
    server.login("apikey", "SG.your-api-key-here")
    server.send_message(msg)

REST API — Direct HTTP calls (preferred for modern applications):

python
# Python (Postmark API)
import httpx

response = httpx.post(
    "https://api.postmarkapp.com/email",
    headers={
        "Accept": "application/json",
        "Content-Type": "application/json",
        "X-Postmark-Server-Token": "your-server-token",
    },
    json={
        "From": "orders@example.com",
        "To": "customer@gmail.com",
        "Subject": "Order Shipped",
        "HtmlBody": "<h1>Your order shipped!</h1>",
        "TextBody": "Your order shipped!",
        "MessageStream": "outbound",
    },
)
typescript
// TypeScript (Resend SDK)
import { Resend } from "resend";

const resend = new Resend("re_your-api-key");

const { data, error } = await resend.emails.send({
  from: "orders@example.com",
  to: "customer@gmail.com",
  subject: "Order Shipped",
  html: "<h1>Your order shipped!</h1>",
});

SMTP relay vs API comparison:

FactorSMTP RelayREST API
Setup complexityMinimal (change SMTP host)Requires code changes
Error handlingLimited (SMTP status codes)Rich (HTTP status + JSON errors)
ThroughputLower (connection overhead)Higher (connection pooling, async)
Metadata/tagsLimitedFull tagging, metadata support
Template renderingClient-sideServer-side (ESP templates)
Best forLegacy apps, quick migrationNew applications, high volume

Template Management

Avoid embedding HTML email templates in application code. Use ESP-side templates with variable substitution:

python
# Postmark template API
response = httpx.post(
    "https://api.postmarkapp.com/email/withTemplate",
    headers={
        "X-Postmark-Server-Token": "your-server-token",
        "Content-Type": "application/json",
    },
    json={
        "From": "orders@example.com",
        "To": "customer@gmail.com",
        "TemplateAlias": "order-shipped",
        "TemplateModel": {
            "order_id": "1234",
            "tracking_url": "https://tracking.example.com/1234",
            "customer_name": "Alice",
        },
    },
)

Benefits of server-side templates:

  • Non-engineers can edit templates without code deploys
  • Consistent rendering across email clients
  • A/B testing built into the ESP
  • Version history and rollback

Webhook Handling

ESPs send webhook events for delivery status. You must handle these to maintain list hygiene:

python
# Flask webhook handler for SendGrid events
from flask import Flask, request, jsonify
import hmac
import hashlib

app = Flask(__name__)
WEBHOOK_SECRET = "your-webhook-verification-key"

@app.route("/webhooks/sendgrid", methods=["POST"])
def handle_sendgrid_webhook():
    # Verify webhook signature
    signature = request.headers.get("X-Twilio-Email-Event-Webhook-Signature")
    timestamp = request.headers.get("X-Twilio-Email-Event-Webhook-Timestamp")
    
    payload = timestamp + request.get_data(as_text=True)
    expected = hmac.new(
        WEBHOOK_SECRET.encode(), payload.encode(), hashlib.sha256
    ).hexdigest()
    
    if not hmac.compare_digest(signature, expected):
        return jsonify({"error": "Invalid signature"}), 403
    
    events = request.get_json()
    for event in events:
        match event["event"]:
            case "bounce":
                # Hard bounce — suppress this address permanently
                suppress_email(event["email"], reason="hard_bounce")
            case "spamreport":
                # Recipient marked as spam — suppress immediately
                suppress_email(event["email"], reason="spam_complaint")
            case "dropped":
                # ESP refused to send (previously bounced/complained)
                log_dropped(event["email"], event.get("reason"))
            case "delivered":
                update_delivery_status(event["email"], "delivered")
            case "open" | "click":
                update_engagement(event["email"], event["event"])
    
    return jsonify({"status": "ok"}), 200

Critical webhook events to handle:

EventAction Required
bounce (hard)Permanently suppress the address
spamreport / complaintPermanently suppress the address
droppedLog and investigate the cause
deliveredUpdate delivery status for tracking
deferredMonitor — may indicate reputation issues

Common Misconceptions

  • "We can just retry bounced addresses next month." Hard-bounced addresses are permanently invalid. Retrying them damages your sender reputation and can get your account suspended by your ESP. Suppress them immediately and permanently.

Practical Setup Walkthrough

Setting Up SPF + DKIM + DMARC from Scratch

Prerequisite: You have DNS access for your domain and use Google Workspace for corporate email and SendGrid for transactional email.

Step 1: Inventory all email senders

Before touching DNS, list every service that sends email as your domain:

ServiceTypeSender Address
Google WorkspaceCorporate email*@example.com
SendGridTransactionalnoreply@example.com
MailchimpMarketingnews@example.com
ZendeskSupportsupport@example.com
Your application serverAlertsalerts@example.com

Step 2: Publish SPF

dns
example.com TXT "v=spf1 include:_spf.google.com include:sendgrid.net include:servers.mcsv.net include:mail.zendesk.com ~all"

Use ~all (softfail) initially instead of -all (hardfail) until you have confirmed all senders are covered.

Step 3: Configure DKIM for each service

For Google Workspace:

  1. Admin Console > Apps > Google Workspace > Gmail > Authenticate Email
  2. Generate DKIM key (2048-bit RSA)
  3. Add the provided TXT record:
dns
google._domainkey.example.com TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..."

For SendGrid:

  1. Settings > Sender Authentication > Authenticate Your Domain
  2. Add the CNAME records provided:
dns
s1._domainkey.example.com CNAME s1.domainkey.u12345.wl.sendgrid.net.
s2._domainkey.example.com CNAME s2.domainkey.u12345.wl.sendgrid.net.

Repeat for each ESP. Every service must have custom DKIM configured.

Step 4: Publish DMARC in monitoring mode

dns
_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; ruf=mailto:dmarc-forensic@example.com; adkim=r; aspf=r"

Step 5: Monitor for 2-4 weeks

Use a DMARC report analyzer (dmarcian, Valimail, EasyDMARC, or PowerDMARC) to process aggregate reports. Look for:

  • Legitimate senders that are failing DKIM or SPF alignment
  • Unknown senders (shadow IT or spoofing attempts)
  • High failure rates from specific IPs

Step 6: Fix failures and tighten policy

After all legitimate senders pass DMARC:

dns
# Week 5: Quarantine 25%
_dmarc.example.com TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc-reports@example.com"

# Week 7: Quarantine 100%
_dmarc.example.com TXT "v=DMARC1; p=quarantine; pct=100; rua=mailto:dmarc-reports@example.com"

# Week 10: Reject
_dmarc.example.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc-reports@example.com; sp=reject"

Step 7: Protect parked/unused domains

For every domain you own that does not send email:

dns
parked-domain.com TXT "v=spf1 -all"
*._domainkey.parked-domain.com TXT "v=DKIM1; p="
_dmarc.parked-domain.com TXT "v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc-reports@example.com"

Testing Tools

ToolURLWhat It Tests
mail-tester.comhttps://mail-tester.comSends a test email and gives a deliverability score out of 10
MXToolboxhttps://mxtoolbox.comSPF, DKIM, DMARC record validation, blocklist checking
Google Admin Toolboxhttps://toolbox.googleapps.com/apps/checkmx/DNS and mail configuration for Google Workspace
dmarcianhttps://dmarcian.comDMARC record inspection, report analysis, deployment guidance
DKIM Validatorhttps://dkimvalidator.comSend a test email to verify DKIM signing is working
Postmaster Tools (Google)https://postmaster.google.comDomain/IP reputation, spam rate, authentication rate for Gmail
SNDS (Microsoft)https://sendersupport.olc.protection.outlook.comIP reputation data for Outlook delivery
Hardenizehttps://hardenize.comFull email security assessment (SPF, DKIM, DMARC, MTA-STS, DANE)

Quick validation commands:

bash
# Check SPF record
dig TXT example.com +short | grep spf

# Check DKIM record (replace 'google' with your selector)
dig TXT google._domainkey.example.com +short

# Check DMARC record
dig TXT _dmarc.example.com +short

# Check MX records
dig MX example.com +short

# Check MTA-STS record
dig TXT _mta-sts.example.com +short

# Full header analysis — send email to this address and check results
# Use check-auth@verifier.port25.com for automated SPF/DKIM/DMARC analysis

Monitoring and Maintaining Over Time

Email security is not a "set it and forget it" configuration. Ongoing maintenance includes:

Weekly:

  • Review DMARC aggregate reports for new failures
  • Check blocklist status (automate with monitoring tools)
  • Review bounce rates and complaint rates in ESP dashboards

Monthly:

  • Verify all SPF includes are still valid (providers change subdomains)
  • Check that SPF lookup count has not exceeded 10 (new includes from IT)
  • Review Google Postmaster Tools and Microsoft SNDS for reputation trends

Quarterly:

  • Rotate DKIM keys
  • Audit the list of authorized senders (remove services no longer in use)
  • Test deliverability by sending to seed addresses across Gmail, Outlook, Yahoo

Annually:

  • Review MTA-STS policy and certificate validity
  • Reassess BIMI certificate (if applicable)
  • Full DNS audit of all email-related records across all domains

When NOT to Use Email

Email is not the right channel for everything. Consider alternatives when:

ScenarioWhy Email Is WrongBetter Alternative
Real-time alertsEmail delivery is asynchronous with no latency guaranteePagerDuty, Slack webhooks, SMS
Two-factor auth codesEmail accounts are a common compromise vector for MFATOTP apps (Authy, Google Authenticator), hardware keys
Large file transferEmail attachments have size limits and lack access controlPresigned S3 URLs, Google Drive, Dropbox
Conversation threadsEmail threading is fragmented and loses contextSlack, Teams, Discord
Machine-to-machineEmail adds unnecessary complexityHTTP webhooks, message queues
Sensitive data deliveryEmail encryption (S/MIME, PGP) has poor UX adoptionSecure portals with access links

Quiz

1. What is the difference between the envelope MAIL FROM and the header From: in an email, and why does this distinction matter for security?

The envelope MAIL FROM is the address used in the SMTP protocol exchange — it controls where bounce messages go and is what SPF validates. The header From: is what the recipient sees in their email client. These can be completely different addresses. This matters because SPF alone only validates the envelope sender, so an attacker can pass SPF with their own domain in the envelope while displaying a spoofed From: header. DMARC alignment closes this gap by requiring the envelope and header domains to match.

2. An SPF record has 11 include mechanisms and DNS lookups. What happens when a receiving server evaluates it, and how do you fix it?

SPF has a hard limit of 10 DNS lookups (RFC 7208). With 11 lookups, the SPF check returns a permerror (permanent error), and most receiving servers treat this as an SPF failure. To fix it: (a) flatten some includes into ip4/ip6 ranges (but these need ongoing maintenance when providers change IPs), (b) use an SPF flattening service that auto-updates, (c) consolidate sending through fewer ESPs, or (d) use subdomains with separate SPF records for different sending services.

3. You configured DKIM for your domain with c=simple/simple canonicalization, and DKIM failures are showing up in DMARC reports for emails sent through a mailing list. Why?

Simple canonicalization requires an exact byte-for-byte match of headers and body (except trailing blank lines in the body). Mailing lists frequently modify email headers (adding list footers, changing subject line prefixes, adjusting whitespace). These modifications break the DKIM signature under simple canonicalization. The fix is to switch to c=relaxed/relaxed, which normalizes whitespace and lowercases header names before hashing, making the signature resilient to minor modifications by intermediaries.

4. Your DMARC record is v=DMARC1; p=reject; aspf=s; adkim=s. Your transactional emails are sent via SendGrid with envelope MAIL FROM: bounce@em123.example.com and DKIM d=em123.example.com, but the header From: is noreply@example.com. Will these emails pass DMARC?

No. With strict alignment (aspf=s and adkim=s), the SPF domain (em123.example.com) and DKIM domain (em123.example.com) must exactly match the From: header domain (example.com). Since em123.example.com is not equal to example.com, neither SPF nor DKIM are aligned, and DMARC fails. The fix is either: (a) switch to relaxed alignment (aspf=r; adkim=r) where organizational domain matching (example.com) is sufficient, or (b) configure SendGrid to use example.com directly in the envelope and DKIM signing.

5. You are migrating from SendGrid to Postmark. What steps are needed to avoid a deliverability drop during the transition?

(1) Configure Postmark DKIM (publish new CNAME records) and update SPF to include both Postmark and SendGrid includes. (2) Keep both providers active in DNS during transition. (3) Warm up Postmark's IPs/sending path by gradually shifting traffic (start with 10%, increase over 2-3 weeks). (4) Monitor DMARC reports for alignment failures on Postmark-sent messages. (5) After full migration, remove SendGrid's SPF include and old DKIM records. (6) Monitor deliverability metrics (bounce rate, spam complaints, inbox placement) closely for 4-6 weeks post-migration.

6. A domain parked-brand.com is registered by your company but never sends email. An employee asks why it needs email authentication records. What do you tell them?

Attackers can send emails with a From: header of ceo@parked-brand.com to phish your customers or partners. Without SPF, DKIM, and DMARC records, receiving servers have no way to know that the domain should never send email, so these spoofed messages may be delivered. The domain needs: v=spf1 -all (no IPs authorized), a DKIM record with an empty public key (p=), and v=DMARC1; p=reject; sp=reject so that any email claiming to come from this domain is rejected by DMARC-enforcing receivers.

7. Your DMARC aggregate report shows 200 emails from an IP address you do not recognize, all passing SPF but failing DKIM. The From: header domain is your domain. Should you be concerned?

Yes, this is suspicious. The emails pass SPF, meaning the IP is listed in your SPF record (either directly or via an include). This could be: (a) a third-party service you authorized in SPF but forgot to configure DKIM for, (b) a compromised server within one of your SPF-included providers, or (c) an attacker exploiting an overly permissive SPF include. Investigate by checking which include the IP belongs to, verify it is a legitimate service, and configure DKIM for it. If it is unauthorized, remove the include and consider narrowing your SPF record. The emails currently fail DMARC only if DKIM is required for alignment — if SPF alignment passes in relaxed mode, DMARC might still pass, which is the worse scenario since the spoofed mail would be delivered.

Exercise

Build a Complete Email Security Configuration

You are the engineer responsible for setting up email security for acme-corp.com. The company uses:

  • Google Workspace for corporate email
  • Postmark for transactional emails (order confirmations, password resets)
  • Mailchimp for marketing newsletters
  • A custom Node.js application server that sends alerts via SMTP

Part 1: DNS Records

Write the complete set of DNS records needed:

  1. SPF record that authorizes all four senders
  2. DMARC record starting in monitoring mode
  3. List the DKIM CNAME/TXT records you would need (use placeholder values)

Part 2: Parked Domains

The company also owns acme-corp.net and acmecorp.com (defensive registrations that never send email). Write the DNS records for these domains.

Part 3: Migration Plan

Six months later, the company decides to switch from Mailchimp to Resend for marketing emails. Write a step-by-step migration plan that:

  • Maintains deliverability during transition
  • Updates all DNS records correctly
  • Includes a warm-up schedule for Resend
  • Defines rollback criteria

Part 4: Monitoring Setup

Design a monitoring checklist:

  • What DMARC report metrics trigger investigation?
  • What bounce rate thresholds require action?
  • How often should DKIM keys be rotated?
  • What tools would you use for ongoing monitoring?

Expected Deliverables:

  • Complete DNS zone file entries for all three domains
  • A migration timeline with specific DNS changes at each step
  • A monitoring runbook with thresholds and escalation criteria

One-Liner Summary: Email authentication is a three-layer retrofit — SPF authorizes sending IPs, DKIM signs messages cryptographically, DMARC enforces alignment between them — and deliverability depends on reputation, engagement, and infrastructure hygiene built on top of that authentication foundation.

"What I cannot create, I do not understand." — Richard Feynman