DeliverabilitySecurityDNS

DKIM, SPF, and DMARC explained for developers

A developer-focused guide to email authentication protocols. Learn what DKIM, SPF, and DMARC do, why they matter, and how to configure them correctly for your sending domain.

Nuntly
March 25, 2026
13 min read

When your application sends an email, the receiving mail server needs to decide whether to trust it. Is this message really from your domain, or is someone impersonating you? Three DNS-based protocols answer that question: SPF, DKIM, and DMARC.

If you have ever seen your transactional emails land in spam despite having valid content, the cause is almost always missing or misconfigured authentication records. This guide explains what each protocol does, how they work together, and how to set them up correctly.

Why email authentication matters

Mailbox providers like Gmail, Outlook, and Yahoo use authentication as a primary signal when deciding where to place your email. Starting in 2024, Gmail and Yahoo began enforcing strict requirements: bulk senders must have valid SPF, DKIM, and DMARC records or their messages will be rejected.

Even if you send low volumes of transactional email, authentication directly affects:

  • Inbox placement: Unauthenticated emails are far more likely to land in spam.
  • Sender reputation: Failed authentication attempts accumulate negative signals against your domain.
  • Protection against spoofing: Without authentication, anyone can send email that appears to come from your domain.

SPF: who is allowed to send

SPF (Sender Policy Framework) is the simplest of the three protocols. The idea: you publish a DNS record on your sending domain that lists every IP address authorized to send email on your behalf. When a receiving mail server gets a message claiming to come from your domain, it looks up that record and checks the sender's IP against the list. If the IP is not listed, the email is considered spoofed and is typically marked as spam or rejected outright.

How SPF works

In practice, SPF verification follows four steps:

  1. You publish a TXT record in your domain's DNS that lists the IP addresses and services authorized to send email for your domain. For example: v=spf1 include:amazonses.com ~all
  2. When a receiving server gets an email claiming to be from your domain, it queries DNS for your SPF record.
  3. It checks whether the sending server's IP address matches one of the authorized mechanisms in the record.
  4. If a match is found, SPF passes. If no mechanism matches, the all qualifier at the end of the record determines the outcome (reject, soft fail, or neutral).

SPF record anatomy

An SPF record is a single DNS TXT record. Every token has a specific role.

v=spf1 ip4:203.0.113.0/24 include:amazonses.com -all

Version tag: v=spf1 is required as the first token. It identifies the record as SPF version 1. Without it, the TXT record is ignored during SPF evaluation.

Mechanisms are evaluated left to right. The first match determines the result.

MechanismWhat it checksExample
ip4Sender IP is in the IPv4 rangeip4:203.0.113.0/24
ip6Sender IP is in the IPv6 rangeip6:2001:db8::/32
includeRecursively check another domain's SPF recordinclude:amazonses.com
aSender IP matches the domain's A/AAAA recorda:mail.acme.com
mxSender IP matches one of the domain's MX host IPsmx
existsPass if a DNS A lookup on the target resolvesexists:%{i}.spf.acme.com

Qualifiers are optional prefixes before a mechanism. They determine the SPF result when that mechanism matches.

QualifierMeaningSPF result
+ (default)Allowpass
-Rejectfail
~Accept but marksoftfail
?No opinionneutral

If you write include:amazonses.com without a prefix, the implicit qualifier is + (pass).

The all mechanism matches every sender IP. It acts as the default rule at the end of the record.

  • -all : strict. Reject anything not explicitly authorized. Use this once all legitimate senders are listed.
  • ~all : permissive. Accept but flag as suspicious. Useful during initial setup while you verify coverage.
  • +all : open. Authorizes every server on the internet to send as your domain. Never use this.
  • ?all : neutral. Equivalent to no SPF record for unmatched senders.

Most production domains should use -all.

SPF evaluation in practice

SMTP connection

MAIL FROM: hello@acme.com

DNS lookup

dig TXT acme.com

Parse SPF record

v=spf1 ... -all

Evaluate mechanisms

Left to right, first match wins

SPF pass

include:amazonses.com matched

SMTP envelope
MAIL FROM:hello@acme.com
RCPT TO:user@recipient.com
Sending IP:54.240.27.42
SPF record evaluation
v=spf1
ip4:203.0.113.0/24
include:amazonses.com
-all
Authentication-Results
spf:pass
sender IP:54.240.27.42
smtp.mailfrom:acme.com

A receiving server gets an email with this SMTP envelope:

  • Sending IP: 54.240.27.42
  • MAIL FROM: hello@acme.com

The server queries DNS for the SPF record:

Shell
dig +short TXT acme.com | grep "v=spf1"

Returns:

"v=spf1 ip4:203.0.113.0/24 include:amazonses.com -all"

Evaluation proceeds left to right. The first match wins:

  1. ip4:203.0.113.0/24 : Is 54.240.27.42 in the 203.0.113.0/24 range (203.0.113.0 to 203.0.113.255)? No. Continue.
  2. include:amazonses.com : The server recursively queries dig +short TXT amazonses.com, retrieves its IP ranges, and checks if 54.240.27.42 is listed. It is. Result: pass.
  3. -all : Not evaluated. A match was already found in step 2.

The receiving server records the result in an email header:

Authentication-Results: mx.recipient.com;
spf=pass (sender IP is 54.240.27.42) smtp.mailfrom=acme.com

You can inspect this header in any received email to verify SPF is working. In Gmail, click "Show original" on any message.

SPF pitfalls to avoid

DNS lookup limit: SPF evaluation allows a maximum of 10 DNS lookups. Each include directive triggers at least one lookup, and nested includes count toward the total. If you use multiple email services, your SPF record can easily exceed this limit, causing silent failures.

Check your lookup count with:

Shell
dig +short TXT yourdomain.com | grep "v=spf1"

Then follow each include and count nested lookups. The total must stay under 10.

Multiple SPF records: You must have exactly one SPF record per domain. If you have two, both are invalid. When adding a new service, merge its include into your existing record rather than creating a second one.

Flattening: If you exceed the lookup limit, you can flatten your SPF record by resolving the includes into explicit IP ranges. However, this requires maintenance because the IPs of your email providers can change. Automated SPF flattening tools can help.

DKIM: was the message altered

Where SPF verifies the sender's identity, DKIM verifies the message itself. The idea: your email provider signs each outgoing email with a private key. The corresponding public key is published in DNS. When a receiving server gets the email, it retrieves the public key and checks the signature. If the signature is valid, the message was not altered in transit. If it fails, the content was modified or the email was forged.

How DKIM works

DKIM verification is a two-phase process. The sending side signs, the receiving side verifies:

  1. When your email provider sends a message, it generates a SHA-256 hash of selected headers (From, To, Subject, Date) and the body.
  2. That hash is encrypted with a private key. The result is attached to the email as a DKIM-Signature header.
  3. The receiving server reads the DKIM-Signature header to extract the domain (d=) and selector (s=).
  4. It queries DNS for the public key at {selector}._domainkey.{domain}.
  5. It decrypts the signature with the public key and compares the hash against the actual message content.
  6. If they match, DKIM passes. If not, the message was altered in transit or the signature was forged.

DKIM-Signature header anatomy

Every DKIM-signed email carries a DKIM-Signature header. Here is what each field means:

DKIM-Signature: v=1; a=rsa-sha256; d=acme.com; s=nuntly;
h=from:to:subject:date:message-id;
bh=2jUSOH9NhtVGCQWNr9BrIA==;
b=dkiM3gHSK2n4L9a...
FieldWhat it contains
vDKIM version (always 1).
aSigning algorithm. rsa-sha256 is standard.
dThe signing domain. Must align with the From domain for DMARC.
sThe selector. Points to the DNS record containing the public key.
hList of headers included in the signature.
bhBase64-encoded hash of the message body.
bBase64-encoded signature of the headers listed in h.

DKIM public key record

The public key is published as a TXT record under a specific selector subdomain:

nuntly._domainkey.yourdomain.com TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEB..."
FieldWhat it contains
vDKIM key version (always DKIM1).
kKey type. rsa is standard.
pBase64-encoded public key.

The selector (nuntly in this example) identifies which key pair to use. A single domain can have multiple selectors for different services, so each provider's DKIM records coexist without conflict.

When you add a domain in Nuntly, the platform generates your DKIM keys and provides the exact DNS records you need to publish. See domain security setup for the guided workflow.

DKIM verification in practice

Sign message

SHA-256 hash + RSA signature

Attach signature

DKIM-Signature header

DNS lookup

nuntly._domainkey.acme.com

Verify signature

Public key decrypts hash

DKIM pass

Message integrity verified

DKIM-Signature header
d=acme.com
s=nuntly
h=from:to:subject:date
bh=2jUSOH9N...
b=dkiM3gHS...
Public key (DNS)
record:nuntly._domainkey.acme.com
v=:DKIM1
k=:rsa
p=:MIGfMA0G...
Authentication-Results
dkim=pass
header.d=acme.com
header.s=nuntly

A receiving server gets an email with this DKIM-Signature header:

DKIM-Signature: v=1; a=rsa-sha256; d=acme.com; s=nuntly;
h=from:to:subject:date:message-id;
bh=2jUSOH9NhtVGCQWNr9BrIA==;
b=dkiM3gHSK2n4L9a...

Verification proceeds:

  1. The server extracts d=acme.com and s=nuntly from the header.
  2. It queries DNS: dig +short TXT nuntly._domainkey.acme.com and retrieves the public key.
  3. It recomputes the hash of the headers listed in h= and the body, then compares it against the signature decrypted with the public key.
  4. The hashes match. Result: pass.

The receiving server records the result:

Authentication-Results: mx.recipient.com;
dkim=pass header.d=acme.com header.s=nuntly

DKIM best practices

  • Use 2048-bit keys: 1024-bit keys are increasingly considered weak. Most providers now default to 2048-bit, which offers stronger protection.
  • Rotate keys periodically: While not required, rotating DKIM keys annually reduces risk if a private key is ever compromised. When rotating, publish the new key first, wait for DNS propagation, then switch the signing configuration.
  • Signed headers: Nuntly signs From, To, Subject, Date, Message-ID, MIME-Version, Content-Type, and Reply-To by default. No configuration needed on your side.

DMARC: the policy layer

SPF and DKIM verify that an email is authentic. But they do not tell the receiving server what to do when verification fails. Should it deliver the email anyway? Send it to spam? Reject it? Without a policy, each receiving server decides on its own, and those decisions vary widely. DMARC fills that gap. It lets you publish a policy that tells receiving servers exactly how to handle unauthenticated emails from your domain. It also sends you reports so you can see who is sending email as your domain and whether authentication is passing.

How DMARC works

DMARC adds two things on top of SPF and DKIM: a policy and an alignment requirement.

  1. The receiving server checks SPF and DKIM results for the incoming email.
  2. DMARC requires at least one of SPF or DKIM to pass and align with the From domain. Passing alone is not enough.
  3. If neither passes with alignment, DMARC applies the policy you defined: none (do nothing), quarantine (send to spam), or reject (block entirely).
  4. The receiving server sends aggregate reports to the address you specified, showing all authentication results for your domain.

Alignment explained

SPF and DKIM can pass without aligning. Alignment means the domain used by the protocol matches the domain in the From header that the recipient sees.

Concrete example: you send an email where the From header is hello@acme.com.

  • SPF alignment: the envelope MAIL FROM domain must match acme.com (or a subdomain in relaxed mode).
  • DKIM alignment: the d= field in the DKIM-Signature must match acme.com (or a subdomain in relaxed mode).

Two alignment modes:

  • Relaxed (the default): subdomains are allowed. mail.acme.com aligns with acme.com.
  • Strict: exact domain match only. mail.acme.com does not align with acme.com.

This is why SPF and DKIM can both pass individually and DMARC still fails. If the domains do not align with the From header, DMARC treats them as unauthenticated.

DMARC record anatomy

DMARC is a TXT record published at _dmarc.yourdomain.com:

_dmarc.yourdomain.com TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@yourdomain.com; pct=100"
ParameterWhat it controls
vVersion. Always DMARC1.
pPolicy: none (monitor only), quarantine (send to spam), reject (block entirely).
ruaAddress to receive aggregate reports (XML format, sent daily by receiving servers).
rufAddress to receive forensic reports (individual failure details, optional).
pctPercentage of messages to apply the policy to. Useful for gradual rollout.
adkimDKIM alignment mode: r (relaxed, default) or s (strict).
aspfSPF alignment mode: r (relaxed, default) or s (strict).

DMARC evaluation in practice

A receiving server gets an email with From: hello@acme.com. It has already checked SPF and DKIM:

  • SPF: pass, envelope domain acme.com aligns with From domain. SPF alignment: pass.
  • DKIM: pass, d=acme.com aligns with From domain. DKIM alignment: pass.

DMARC requires at least one to pass with alignment. Both do. Result: DMARC pass.

The server queries the DMARC policy:

Shell
dig +short TXT _dmarc.acme.com

Returns "v=DMARC1; p=reject; rua=mailto:dmarc@acme.com". Since DMARC passed, the policy is not applied. The email is delivered normally.

If DMARC had failed, p=reject would instruct the server to block the email entirely.

The receiving server records the full result:

Authentication-Results: mx.recipient.com;
spf=pass smtp.mailfrom=acme.com;
dkim=pass header.d=acme.com header.s=nuntly;
dmarc=pass header.from=acme.com

Do not jump straight to p=reject. Follow this progression:

  1. Start with p=none: This monitors authentication without affecting delivery. Collect reports for at least two weeks.
  2. Review your reports: Identify all legitimate sources sending email for your domain. Make sure each one has proper SPF and DKIM records.
  3. Move to p=quarantine: Unauthenticated messages go to spam. Monitor for false positives.
  4. Advance to p=reject: Unauthenticated messages are blocked entirely. This is the strongest protection against spoofing.
_dmarc.yourdomain.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc-reports@yourdomain.com; pct=100"

How the three protocols work together

Each protocol covers a different aspect of email authentication:

  • SPF checks who sent the message (server identity).
  • DKIM checks what was sent (message integrity).
  • DMARC enforces a policy when either check fails and verifies alignment between protocols and the visible From domain.

A properly configured domain has all three. Here is a complete DNS setup:

; SPF
yourdomain.com TXT "v=spf1 include:amazonses.com ~all"
; DKIM
nuntly._domainkey.yourdomain.com TXT "v=DKIM1; k=rsa; p=MIGfMA0G..."
; DMARC
_dmarc.yourdomain.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com"

Verifying your configuration

After publishing your records, verify them using command-line tools:

Shell
# Check SPF
dig +short TXT yourdomain.com | grep "v=spf1"
# Check DKIM (replace nuntly with your selector)
dig +short TXT nuntly._domainkey.yourdomain.com
# Check DMARC
dig +short TXT _dmarc.yourdomain.com

In Nuntly, the domain security page shows real-time verification status for each DNS record. Once all records propagate, your domain is marked as fully authenticated and ready to send.

Next steps

With SPF, DKIM, and DMARC properly configured, your transactional emails have the authentication foundation they need to reach the inbox reliably. Pair this with delivery monitoring to track your ongoing sender reputation and catch issues early.

Ready to get started?

Ship emails, not infrastructure

Free plan available. No credit card required.

Start sending free