23 years from Stockholm
Operator notes · From the Stockholm desk

DKIM signing for transactional email: a practical setup guide

Cryptographic signing is becoming table stakes for any serious sender in 2012. A working walk-through — selectors, key generation, DNS publication, MTA configuration, header selection and verification against the major receivers.

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-Results header. A clean dkim=pass with the expected header.i=@yourdomain is 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.

DKIM signature tags as defined in RFC 6376 — what each one does
TagRequired?MeaningOperational note
v=YesDKIM version — always 1If absent, signature is invalid by definition
a=YesSigning algorithm — typically rsa-sha256Use rsa-sha256; rsa-sha1 is permitted but weakening
d=YesSigning domainThis is the durable identity. DMARC alignment compares this to the From-header domain
s=YesSelectorCombines with d= to form the DNS lookup: s._domainkey.d
c=No (default simple/simple)Canonicalization for header/bodySet to relaxed/relaxed in production; defaults are too strict
h=YesList of headers covered by the signatureFrom, To, Subject, Date, Message-ID at minimum; never sign Received or Return-Path
bh=YesHash of the canonicalized bodyMismatch means the body changed in transit (or canonicalization is wrong)
b=YesSignature itself (base64)Mismatch means a header changed, or the wrong key was used
t=NoSignature timestamp (Unix time)Optional; useful for replay-attack detection if combined with x=
x=NoSignature expiration timestampOptional; rarely used. If set and exceeded, signature is treated as expired
l=NoBody length covered by the signatureLeave OFF unless mailing-list survival is a hard requirement; setting it allows appended content to remain valid
i=NoIdentity (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 keyAlways 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

The four canonicalization combinations and what they survive
ModeHeader changes toleratedBody changes toleratedWhen to use
simple/simpleNone — any whitespace change breaks signatureNone — only trailing empty lines are normalizedInternal-only mail flows where no intermediary touches the message
simple/relaxedNone on headersInternal whitespace runs collapsed; trailing whitespace strippedRarely used; pick relaxed/relaxed instead
relaxed/simpleHeader field name case-insensitive; whitespace normalized; folded headers unfoldedNone on bodyRarely used
relaxed/relaxedSame as relaxed for headersInternal whitespace runs collapsed; trailing whitespace strippedThe 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.

DKIM verification flow — what happens between send and inbox The signing-and-verification round trip From the sender's MTA through DNS to the receiver's filter chain Sender MTA Computes signature over headers + body DKIM-Signature header added Network Message in transit (possibly modified) Receiver MTA Reads d= and s= from signature DNS lookup s._domainkey.d → public key Verify Recompute hash; compare to b= Auth-Results dkim=pass / fail attached to message
Six steps, two of them DNS-dependent. The most common failure points are step 4 (DNS record not yet propagated, key formatting wrong) and step 5 (header rewriting in transit, body modification by a gateway, canonicalization mismatch). Reading the receiver's Authentication-Results header in step 6 tells you which step failed.
The "we'll just regenerate the key" mistake The temptation, when DKIM verification fails after a configuration change, is to regenerate the key and republish DNS as the first move. This almost always makes things worse. The original key was probably fine — the failure is usually in canonicalization, header selection, signing-vs-modification ordering, or DNS publication of the existing key. Regenerating means a new selector, a new DNS record, propagation delay, and now two failure modes layered on top of each other. The right first move is always to read the 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

Common Authentication-Results outputs and their typical causes
Result lineLikely causeFirst place to look
dkim=pass header.i=@example.comWorking as intendedNothing — verify From-domain alignment if planning DMARC adoption
dkim=permerror (key not found)DNS record absent or selector typodig +short TXT selector._domainkey.domain from multiple resolvers
dkim=fail (body hash did not verify)Body modified in transit, or canonicalization wrongSwitch 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 signConfirm signing key matches DNS-published public key; check for header rewriting at egress
dkim=neutral (no signature)MTA didn't sign at allCheck 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 unavailableUsually transient; if persistent, check authoritative DNS health for the signing domain
dkim=pass header.i=@subdomain.example.com when expecting parent domainSigning on subdomain rather than organizational domainDecide 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 valueEither 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.