DKIM has been around in production deployment since Yahoo and Cisco merged DomainKeys and Identified Internet Mail in 2007, but 2012 is the year it stops being optional and starts being expected. Three things converged to push it across that line. First, Gmail, Yahoo, AOL and Hotmail all began weighting DKIM signatures heavily in their delivery decisions over the past 18 months. Second, RFC 6376 was published last September, replacing the original DKIM RFC 4871 with a clean reference implementation that closed several ambiguities and gave receivers a stable target to verify against. Third — and this is the catalyst — the DMARC specification published just five weeks ago, on January 30, depends entirely on DKIM and SPF alignment to function. Senders who skip DKIM are now skipping the foundation that the next two years of email authentication policy will be built on. This guide is the practical setup walkthrough: selectors, key generation, DNS publication, MTA configuration on the three MTAs that matter (Postfix, Exim, PowerMTA), header selection, canonicalization, and verification against the major receivers.
Key takeaways
- DKIM attaches a cryptographic signature to every outbound message, covering selected headers and the body. Receivers fetch the public key from DNS, verify the signature, and use the result as a durable identity signal that survives infrastructure changes (unlike SPF, which is tied to specific sending IPs).
- Selector strategy is the part most teams underplan. Use distinct selectors per sending source (transactional relay, marketing platform, ESP) and rotate annually. The selector lives in the DNS lookup at
selector._domainkey.domain; rotation means publishing a new selector and switching signing without breaking verification of in-flight messages. - Use 1024-bit RSA keys at minimum. The 768-bit RSA factorization in 2010 closed that conversation. 2048-bit is the right choice for new deployments where the MTA and DNS infrastructure can handle it cleanly — the only complication is the 255-character TXT-string limit, which forces longer keys to be split across multiple quoted strings.
- Sign From, To, Subject, Date, and Message-ID at minimum. Use relaxed/relaxed canonicalization unless you have a specific reason to be strict. Leave the
l=body-length tag off for transactional and marketing senders — setting it lets attackers append content to signed messages without invalidating the signature. - Verify by sending test messages to addresses you control at Gmail, Yahoo, and Hotmail, then reading the
Authentication-Resultsheader. A cleandkim=passwith the expectedheader.i=@yourdomainis the only signal worth trusting; anything else needs investigation before production traffic flows through the new signing path.
Why DKIM moved from optional to expected
DomainKeys Identified Mail has been in production deployment at the major receivers since 2007, but 2012 is the year it becomes genuinely table stakes. Gmail, Yahoo, AOL and Hotmail all factor DKIM into delivery decisions, the brand-new DMARC specification published in January requires DKIM alignment to function usefully, and anyone sending transactional mail without DKIM signatures is watching their transactional messages get treated with more suspicion than they need to be.
DKIM works by attaching a cryptographic signature to every outbound message. The signature covers selected message headers and the message body. A receiving server fetches the public key for the signing domain from DNS, verifies that the signature matches the content, and can then confirm two things: the message has not been tampered with in transit, and the signature came from someone who controls DNS for the domain in question.
That second property is the one that matters for reputation. Unlike the sending IP, which changes if your relay provider reassigns it, the signing domain is durable. Inbox providers use it to attach a long-term trust record to a sender, independent of infrastructure. This is why DKIM is becoming the anchor for sender identity.
This guide is practical rather than theoretical. It walks through choosing a selector, generating keys, publishing DNS records, configuring signing at the MTA layer and verifying that receivers actually see the signature. The reader assumed here is an engineer comfortable with SMTP fundamentals — if you need a refresher on the underlying protocol that DKIM signatures travel over, the SMTP fundamentals for application developers piece from last year is still the right starting point.
Selectors, keys and the DNS layout
A DKIM deployment starts with a selector, which is simply a name that lets you rotate keys without breaking verification. The selector combines with the signing domain to form the DNS lookup. If your signing domain is example.com and your selector is mail2012, the receiver fetches the public key from mail2012._domainkey.example.com.
Selectors let you run multiple keys at the same time — one for your transactional relay, one for your marketing platform, one for an ESP you use for product updates — without them interfering. They also let you rotate keys by publishing a new selector, switching signing over and retiring the old one, which should be part of annual hygiene.
Key size has become contentious. Historically 512-bit and 768-bit keys were common; a 768-bit RSA key was factored publicly in 2010 and the mood has shifted decisively. Use at least 1024 bits. The major receivers are increasingly hostile to short keys, and the upgrade cost is trivial.
# Generate a 1024-bit RSA private key
openssl genrsa -out mail2012.private 1024
# Extract the public key for DNS publication
openssl rsa -in mail2012.private -pubout -out mail2012.public
cat mail2012.public
The public key goes into a DNS TXT record. The record format is v=DKIM1; k=rsa; p=MIGfMA0... where p= is the base64-encoded key stripped of the header and footer lines and all whitespace. DNS TXT records have a 255-character per-string limit, so longer keys must be split into multiple quoted strings on the same record line.
The DKIM signature header, tag by tag
Once signing is in place, every outbound message carries a DKIM-Signature header with a defined set of tags. Understanding what each tag means is operationally useful: when verification fails, the diagnostic almost always points to one specific tag, and knowing which one tells you where to look.
| Tag | Required? | Meaning | Operational note |
|---|---|---|---|
v= | Yes | DKIM version — always 1 | If absent, signature is invalid by definition |
a= | Yes | Signing algorithm — typically rsa-sha256 | Use rsa-sha256; rsa-sha1 is permitted but weakening |
d= | Yes | Signing domain | This is the durable identity. DMARC alignment compares this to the From-header domain |
s= | Yes | Selector | Combines with d= to form the DNS lookup: s._domainkey.d |
c= | No (default simple/simple) | Canonicalization for header/body | Set to relaxed/relaxed in production; defaults are too strict |
h= | Yes | List of headers covered by the signature | From, To, Subject, Date, Message-ID at minimum; never sign Received or Return-Path |
bh= | Yes | Hash of the canonicalized body | Mismatch means the body changed in transit (or canonicalization is wrong) |
b= | Yes | Signature itself (base64) | Mismatch means a header changed, or the wrong key was used |
t= | No | Signature timestamp (Unix time) | Optional; useful for replay-attack detection if combined with x= |
x= | No | Signature expiration timestamp | Optional; rarely used. If set and exceeded, signature is treated as expired |
l= | No | Body length covered by the signature | Leave OFF unless mailing-list survival is a hard requirement; setting it allows appended content to remain valid |
i= | No | Identity (typically a user-level identifier) | Most senders leave this off; if set, must be a subdomain of d= |
q= | No (default dns/txt) | Query method for fetching the public key | Always dns/txt in 2012; reserved for future extension |
The interplay between d=, s=, and the actual DNS record is where most operational mistakes live. The sending MTA computes the signature using a private key, marks the message with d=example.com s=mail2012, and the receiver fetches mail2012._domainkey.example.com to verify. If any of those three pieces is wrong — selector typo, key out of sync with DNS, signature computed against a different domain than what the From header advertises — verification fails. The diagnostic chain is short, which is the redeeming feature when something goes wrong: read the Authentication-Results header at a major receiver, and the failure mode is usually obvious.
Configuring signing at the MTA
Most senders sign at the boundary — the relay or MTA that hands messages to the outside world. Signing inside the application means every sending service has to manage keys, which is worse operationally than signing once at the point where all mail converges.
On a Postfix plus OpenDKIM setup, the configuration looks roughly like this:
# /etc/opendkim.conf
Domain example.com
KeyFile /etc/opendkim/keys/mail2012.private
Selector mail2012
Socket inet:12301@localhost
Canonicalization relaxed/relaxed
SignatureAlgorithm rsa-sha256
# /etc/postfix/main.cf additions
smtpd_milters = inet:localhost:12301
non_smtpd_milters = inet:localhost:12301
milter_default_action = accept
milter_protocol = 2
PowerMTA and commercial MTAs have their own signing configuration, which is typically richer — per-vMTA selectors, multiple keys, domain-level routing of signatures. The principles are the same: define a selector, reference a key file, specify which headers to sign, pick a canonicalization mode.
On canonicalization: "relaxed/relaxed" is the sensible default. It tolerates the minor whitespace and line-folding changes that happen when messages pass through mailing lists or some corporate gateways. "simple/simple" is stricter and more likely to produce DKIM failures when anything touches the message in transit. Use relaxed unless you have a specific reason not to.
Canonicalization modes — what each one tolerates
| Mode | Header changes tolerated | Body changes tolerated | When to use |
|---|---|---|---|
simple/simple | None — any whitespace change breaks signature | None — only trailing empty lines are normalized | Internal-only mail flows where no intermediary touches the message |
simple/relaxed | None on headers | Internal whitespace runs collapsed; trailing whitespace stripped | Rarely used; pick relaxed/relaxed instead |
relaxed/simple | Header field name case-insensitive; whitespace normalized; folded headers unfolded | None on body | Rarely used |
relaxed/relaxed | Same as relaxed for headers | Internal whitespace runs collapsed; trailing whitespace stripped | The production default for almost every sender. Survives most legitimate mail-path transformations |
The reason relaxed/relaxed wins almost every time is that real mail paths in 2012 routinely add whitespace, fold long header lines, or normalize line endings between CRLF and LF. Strict canonicalization treats any of those as tampering and invalidates the signature. Relaxed canonicalization tolerates them while still detecting genuine content modifications. The only legitimate case for stricter modes is internal-only mail where every hop is under your control — which is rare in production.
Exim 4.77 native DKIM signing
Exim has had native DKIM signing since version 4.70 (2009), which removes the need for a separate milter. The configuration is concise:
Exim 4.77 — outbound transport with DKIM signing# /etc/exim/exim.conf — transport definition
remote_smtp:
driver = smtp
# DKIM signing (Exim native, since 4.70)
dkim_domain = $sender_address_domain
dkim_selector = mail2012
dkim_private_key = /etc/exim/dkim/${dkim_selector}.private
dkim_canon = relaxed
dkim_strict = false
# Optional: restrict signing to specific domains
dkim_sign_headers = From:To:Subject:Date:Message-ID:\
MIME-Version:Content-Type:\
Content-Transfer-Encoding:Reply-To
The variable $sender_address_domain means Exim signs each message with whatever domain appears in the envelope sender. For multi-tenant or multi-domain configurations, this is convenient but requires a key file per signing domain. For single-domain senders, hard-code the domain instead and reference one private key.
PowerMTA 4.0 DKIM signing
PowerMTA 4.0 — DKIM signing per sending domain# /etc/pmta/config
# Per-domain DKIM key configuration
<domain-key mail2012,example.com>
private-key /etc/pmta/dkim/example.com.mail2012.pem
</domain-key>
<domain-key mail2012,news.example.com>
private-key /etc/pmta/dkim/news.example.com.mail2012.pem
</domain-key>
# Global signing configuration
dkim-sign true
dkim-identity $envelope-sender-domain
# Per-domain header signing list
<domain *>
dkim-sign-headers From,To,Subject,Date,Message-ID,
MIME-Version,Content-Type,
Content-Transfer-Encoding,Reply-To
</domain>
Note the domain-key directive takes selector,domain as its identifier — this is what lets PowerMTA match incoming messages against the right key based on what's in the envelope. The pattern of one key file per (selector, signing-domain) combination scales cleanly to dozens of sending domains, which is exactly what teams hit PowerMTA for in the first place.
Which headers to sign, and why it matters
The DKIM signature covers a list of header fields plus the body. Under-signing creates replay and spoofing risks. Over-signing creates fragility — any gateway that rewrites one of the signed headers breaks verification. The balance most senders settle on is:
- From — always. This is what the user sees and what DMARC alignment will check.
- To, Subject, Date, Message-ID — always. These are the semantic identity of the message.
- Reply-To, Sender, MIME-Version, Content-Type, Content-Transfer-Encoding — where relevant.
- Body — sign it, usually with
l=unset so the entire body is covered.
Avoid signing headers that receivers or intermediaries routinely add or rewrite. Return-Path is inserted by receiving MTAs. Received chains grow as the message traverses servers. Do not sign those.
One subtle point on the l= body length tag. Setting it allows messages to survive mailing-list footer appends, but it also means anyone can append content to a signed message without breaking the signature. For transactional and marketing senders, leave l= off. For senders who produce mail that will be reposted to lists, setting l= is a defensible trade-off.
Verifying the signature from the outside
After configuration, send a test message to an address you control at Gmail, Yahoo and Hotmail. View the full message source and look at the Authentication-Results header inserted by the receiver:
Authentication-Results: mx.google.com;
dkim=pass header.i=@example.com;
spf=pass smtp.mailfrom=notify@example.com;
dkim-adsp=pass
A dkim=pass line with header.i=@example.com confirms the signature is valid and the signing domain is what you expect. Anything else — dkim=fail, dkim=neutral, dkim=permerror — needs investigation.
Common failure causes: DNS record not yet propagated, key formatting wrong (header/footer lines or whitespace left in the public key), canonicalization mismatch, selector typo, MTA using a different key than DNS advertises. The good news is the failure mode is usually obvious once you look at the Authentication-Results header.
For ongoing validation, services like dkimcore.org and the older dkim.org test reflectors let you send a message and receive a diagnostic reply. Use them after any change to the signing configuration. The output of these reflectors complements the broader operational practice of monitoring per-IP and per-domain authentication pass rates as part of overall sender reputation work — DKIM is one of several inputs into how receivers form an opinion about a sender, and the framework for understanding the rest is covered in sender reputation fundamentals.
Authentication-Results header in step 6 tells you which step failed.Authentication-Results header from a real receiver and identify which specific tag is rejecting. Replace the key only as a last resort, after the configuration is confirmed correct against the original key.
DKIM verification failures and what they mean
| Result line | Likely cause | First place to look |
|---|---|---|
dkim=pass header.i=@example.com | Working as intended | Nothing — verify From-domain alignment if planning DMARC adoption |
dkim=permerror (key not found) | DNS record absent or selector typo | dig +short TXT selector._domainkey.domain from multiple resolvers |
dkim=fail (body hash did not verify) | Body modified in transit, or canonicalization wrong | Switch to relaxed/relaxed; check whether a content filter is rewriting the body |
dkim=fail (signature did not verify) | Signed header rewritten in transit, or wrong key used to sign | Confirm signing key matches DNS-published public key; check for header rewriting at egress |
dkim=neutral (no signature) | MTA didn't sign at all | Check MTA logs; confirm signing milter or native config is active for this transport |
dkim=temperror (DNS lookup error) | DNS resolver issue at receiver, or record temporarily unavailable | Usually transient; if persistent, check authoritative DNS health for the signing domain |
dkim=pass header.i=@subdomain.example.com when expecting parent domain | Signing on subdomain rather than organizational domain | Decide deliberately — subdomain signing is fine, but DMARC alignment will require matching policy |
dkim=policy (key revoked) | Public key DNS record exists but contains p= with empty value | Either the key was deliberately revoked, or the DNS record is malformed |
The diagnostic chain is short by design. RFC 6376 specifies these failure modes precisely so that "verification failed" is never the whole story — there's always a more specific code, and that code points at one of a small number of root causes. Build the operational habit of reading the Authentication-Results header in detail rather than skimming it, and most DKIM failures will resolve in minutes rather than hours.
DKIM deployment checklist
- Selector chosen and documented (rotate annually).
- Private key stored securely outside source control, readable only by the signing process.
- Public key published as DNS TXT record at
selector._domainkey.domain. - Key size at least 1024 bits; 2048 bits if the signing infrastructure supports it without issue.
- Canonicalization set to relaxed/relaxed.
- Signed headers include From, To, Subject, Date, Message-ID at minimum.
l=body-length tag left off unless messages will traverse mailing lists.- Verification tested against Gmail, Yahoo and Hotmail by reading Authentication-Results.
- Key rotation procedure documented so anyone on the team can do it.
- Monitoring in place so a drop in dkim=pass rate becomes visible quickly.
Frequently asked questions
Do I need DKIM if I already have SPF? Yes. They solve related but different problems. SPF authorizes sending IPs; DKIM attaches a durable signature to the message. Both are inputs to reputation and both will be needed for DMARC when that specification sees wider adoption.
How often should I rotate keys? Annually at minimum. When a key is rotated, publish the new selector, switch signing, wait a few days, then deauthorize the old key in DNS. This way receivers still holding cached results for old messages can verify them until they age out.
What about subdomain signing? If you send transactional mail from mail.example.com and marketing mail from news.example.com, each subdomain can have its own DKIM record. This is generally wiser than signing everything as example.com because it isolates reputation by stream.
Will DKIM hurt delivery if it fails? By itself, no — failed DKIM at a receiver without a DMARC policy will usually be treated as neutral. Once DMARC policies become widespread (which is likely within a few years), a failed DKIM signature will matter much more.
Should I sign with the organizational domain or a subdomain? Both work. Signing with the organizational domain (example.com) gives you a single durable identity that all sending sources contribute to. Signing with subdomains (mail.example.com for transactional, news.example.com for marketing) lets each stream develop independent reputation, which is useful when one stream might damage the other. The mainstream practice in 2012 is moving toward subdomain signing for senders large enough to have multiple streams, and organizational-domain signing for everyone else.
What if my ESP signs with their own domain instead of mine? Common pattern from older ESPs — they DKIM-sign messages with their own infrastructure domain (something like mail.esp-provider.com) rather than your sending domain. This still produces dkim=pass, but the header.i doesn't align with your From header, which means the signature contributes nothing to your domain's reputation. Most modern ESPs will sign with your domain if you publish a DKIM record they can use; if yours doesn't, push on them. With DMARC arriving, ESP-domain signing will stop being acceptable practice.
Is the original DomainKeys (Yahoo's predecessor to DKIM) still worth deploying? No. DomainKeys is deprecated. RFC 4870, which described it, was superseded by the DKIM RFC chain. Some legacy deployments still produce DomainKey-Signature headers alongside DKIM-Signature headers, but the major receivers now ignore DomainKeys in favor of DKIM. Drop DomainKeys when you have the chance to clean up signing configuration.
Where DKIM is heading
The direction is clear. Larger receivers will start treating DKIM alignment as a hard requirement for reputation, not a nice-to-have. The brand-new DMARC specification is built entirely on DKIM and SPF alignment, and the first production deployments of DMARC are already appearing. Within two or three years, DKIM signing will be a precondition for serious delivery at every major inbox provider. The work to get it right now is an investment that pays out steadily for the rest of the decade — and the operational discipline of getting it right once, with proper monitoring in place, costs far less than retrofitting it under pressure when a major receiver finally enforces.
Continue your evaluation
If this article maps to the sending layer you are building or operating, the pages below go deeper into the commercial and operational side of the same territory.