Chapter 02 · Authentication
RFC 6376

DKIM — selectors, rotation, and the canonicalization trap.

DKIM is defined by RFC 6376, with signing-algorithm updates in RFC 8301 and Ed25519 support in RFC 8463. It is the protocol senders most often deploy correctly on day one and then quietly break six months later — through a key rotation performed without overlap, a canonicalization choice that interacts badly with downstream forwarders, or a third-party signing arrangement that fragments DMARC alignment in ways nobody audits until a campaign produces zero opens.

What DKIM actually proves

DKIM attaches a cryptographic signature to outbound mail. The signature covers a specified set of message headers — including the visible From: header — and a hash of the message body. The signing domain publishes its public key in DNS at a deterministic subdomain. A receiver, on inbound, reads the DKIM-Signature: header, retrieves the public key, recomputes the canonical hash of the headers and body, and verifies the signature.

The cryptographic claim is narrow but useful: this message, with these specific headers and this specific body, was signed by an entity holding the private key for this selector at this domain. It does not prove identity of the sending IP, does not prove envelope authenticity, and does not by itself prove that the visible From: header is honest. The alignment of the signing domain with the visible From: domain is what DMARC (Chapter 3) evaluates afterward; DKIM on its own simply produces a verifiable signature anchored in DNS.

That narrowness is the reason most production failures in DKIM are not cryptographic at all. They are deployment failures: the selector record is wrong, the signing key was rotated without overlap, the canonicalization choice does not survive forwarders, the third-party platform signs under its own domain and the operator never notices that DMARC alignment passes only by accident.

The record format

A DKIM public key is published as a TXT record at selector._domainkey.example.com, where selector is an arbitrary label chosen by the operator. The record is a semicolon-delimited string of tag-value pairs:

s1._domainkey.example.com.  IN  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOC..."

The mandatory tags are minimal. v=DKIM1 is the version, currently the only valid value. k= is the key type — rsa for RFC 6376 and RFC 8301 deployments, ed25519 for RFC 8463. p= is the base64-encoded public key. The optional t=y flag marks the key as test-mode and instructs receivers to treat verification failures as informational only; it is useful exactly once, during initial cutover, and is the source of a recurring deployment error described later.

The selector label has no semantic meaning to the protocol. The operator chooses it. Common conventions are s1, s2 for rotation pairs, mail, marketing, transactional for purpose-keyed signing, or date-stamped labels like 202606 for keys whose rotation cadence is tracked in the label itself. Any of these is acceptable. What is not acceptable, and surprisingly common, is reusing a single selector name across all environments and signing pipelines such that the operator cannot tell, from a failed DKIM verification, which signing system was responsible.

Selectors and why operators run multiple

A single sending domain can publish as many selectors as the operator chooses to maintain. The DKIM-Signature header on a given message names exactly one selector via the s= tag, and the receiver retrieves precisely that key. There is no enumeration, no fallback, no implicit chain — the signer commits, in the header it writes, to which key the receiver should fetch.

The practical reasons to run multiple selectors are operational, not cryptographic. A sender running a corporate mail platform, a marketing platform, and a transactional service typically has three distinct signing systems, each holding its own private key, each signing under its own selector. This separation isolates blast radius — a key compromise on the marketing selector does not require rotating the transactional key, and an outage of one signing system does not impair the others.

The rotation cadence the protocol does not specify but the operational community has converged on is roughly: annual rotation as a minimum, quarterly rotation for high-volume senders, and immediate rotation on any suspected key exposure. Senders that have not rotated a DKIM key in three or more years are not technically out of spec, but they are at the long tail of the deployment distribution and increasingly at risk of brute-force attacks against the older 1024-bit keys those long-lived selectors typically hold.

Key length — 1024 vs 2048 vs Ed25519

The original DKIM specification permitted 512- to 2048-bit RSA keys. The historical default of 1024 bits exists for a reason that is almost an accident of DNS protocol design: a TXT record containing a 2048-bit RSA public key, encoded in base64, exceeds the 255-byte single-string limit and exceeds the 512-byte UDP DNS response size that older resolvers would handle without falling back to TCP. The 1024-bit key fit neatly under both limits; the 2048-bit key required string concatenation in the zone file and depended on resolvers correctly handling EDNS0 or TCP retry.

Modern DNS infrastructure handles both transparently. EDNS0 negotiation is universal across major resolvers, TCP fallback works reliably, and authoritative DNS providers concatenate multi-string TXT records correctly on the way out. The historical justification for 1024-bit keys is no longer operationally relevant.

RFC 8301, published in 2018, deprecated SHA-1 and mandated a 1024-bit minimum with 2048-bit recommended. Major mailbox providers now treat 1024-bit RSA as the floor and increasingly flag it in postmaster diagnostics; 2048-bit RSA is the appropriate baseline for any sender provisioning a new selector in 2026. Senders still publishing 1024-bit keys are not failing verification, but they are sitting on a known soft surface that a sufficiently motivated attacker can factor on commodity cloud hardware in a finite, declining amount of compute time.

Ed25519 signing under RFC 8463 produces dramatically smaller keys — a 256-bit Ed25519 public key encodes in 44 bytes of base64, comfortably under every DNS size constraint — and dramatically smaller signatures. The catch is verifier support. Ed25519 is implemented across the major mailbox providers but not universally across the long tail of inbound mail handlers; the operationally safe deployment is dual signing, publishing both an RSA selector and an Ed25519 selector and signing every outbound message with both. This is documented in the multi-signature section below.

Canonicalization — the forwarder trap

A signature over an email message can only verify if the message arrives at the receiver byte-for-byte as it was signed. Email in transit does not, in general, arrive byte-for-byte as it was signed. Intermediate mail handlers rewrite whitespace, fold long header lines, change line-ending conventions between CRLF and LF, append warning banners, and otherwise mutate the message in ways that have no semantic effect on the human reader but that break any naive cryptographic hash.

RFC 6376 defines two canonicalization algorithms per signature scope — one for headers, one for the body — and the signer commits to a pair via the c= tag. The values are simple and relaxed. simple means: hash the content essentially as-is, tolerating only trailing empty lines in the body. relaxed means: collapse runs of whitespace, lowercase header names, strip trailing whitespace from header values, and tolerate the kinds of cosmetic rewriting that normal mail handlers perform.

The default pair is simple/simple. The pair that survives contact with the real internet is relaxed/relaxed. A signer choosing simple/simple in 2026 is making a configuration error: any whitespace-rewriting forwarder, any mailing-list server, any corporate mail gateway that touches the body breaks the signature, and DKIM verification fails at the eventual receiver. The relaxed canonicalization exists precisely to tolerate this class of rewriting and is the appropriate default for outbound mail in any realistic threat model.

Operators occasionally deploy relaxed/simple or simple/relaxed with the intent of strict-signing the body while relaxing the headers, or vice versa. There is no strong reason to do this, and the asymmetric choices interact in unintuitive ways with the multi-signature deployment described below.

The signature header in detail

The DKIM-Signature: header attached to a signed message contains the parameters the receiver needs to reproduce the verification. A representative header, line-folded for readability:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
  d=example.com; s=s1;
  h=from:to:subject:date:message-id;
  bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=;
  b=Jk2pXr0...

The tags decompose as follows. v=1 is the DKIM version. a= is the signing algorithm — rsa-sha256 per RFC 8301, or ed25519-sha256 per RFC 8463. c= is the canonicalization pair. d= is the signing domain — the domain under which the public key is published, and the domain DMARC will evaluate for alignment against the visible From:. s= names the selector. h= lists the headers covered by the signature, in the order they were signed. bh= is the base64-encoded hash of the canonical body. b= is the base64-encoded signature over the canonicalized headers and the bh value itself.

The h= tag is where deployment errors concentrate. A signature that does not cover From: is non-compliant and rejected by most modern verifiers. A signature that does not cover Subject: permits an attacker to rewrite the subject after signing without invalidating the signature — a security hole, not a deliverability concern, but observable in audit. A signature that over-specifies — covering every header the signer can identify, including transient ones like X-Mailer: or Received: — produces signatures that fail verification at receivers whose intermediate handlers added or modified those headers. The conservative selection is: from, to, cc, subject, date, message-id, reply-to, and mime-version, with nothing transient included.

Multi-signature messages

A message can carry more than one DKIM signature. Each signature is an independent DKIM-Signature: header, and the receiver verifies each one independently. A single verifying signature is sufficient to claim DKIM-authentication of the named domain.

The operational reasons to attach multiple signatures are several. A sender running both an RSA-2048 selector and an Ed25519 selector signs each outbound message with both, ensuring that any verifier supporting either algorithm can validate the message. A sender on the cutover week between two RSA selectors signs with both during the overlap, ensuring that any cached or in-flight DNS lookup of the old selector continues to verify until the cache has expired. A sender whose mail traverses two distinct sending systems — a corporate mail platform that signs on departure and a third-party platform that signs on its own way out — may end up with signatures from two different domains attached to the same message.

DMARC alignment is evaluated independently against each signature. A message carrying one signature from d=example.com and another from d=platform.example, with From: hello@example.com, achieves DMARC alignment via the first signature regardless of whether the second signature also verifies. This is the operational basis of the third-party signing model below.

Third-party signing — d= and the alignment question

Sequencing platforms, transactional providers, and CRM-integrated mail systems all face the same architectural question: when signing outbound mail on behalf of a tenant, do they sign under their own platform domain or under the tenant's domain?

The platform-domain pattern signs with d=platform.example and publishes the corresponding selector under the platform's own DNS. The advantage to the platform is operational simplicity: one selector, one key, rotation under the platform's own control. The disadvantage to the sender is that DKIM verification proves only that the platform signed the message — it does not align with the sender's own From: domain, and DMARC alignment via DKIM fails. The sender's DMARC enforcement then depends entirely on SPF alignment, which constrains the sender's envelope configuration in ways operators frequently do not realize until DMARC is escalated to p=reject.

The tenant-domain pattern signs with d=sender.com and requires the sender to delegate a selector subdomain via CNAME — typically platform1._domainkey.sender.com pointing to platform1.dkim.platform.example. The CNAME chains to a TXT record under the platform's control, which contains the actual public key. The sender retains DMARC alignment via DKIM, and the platform retains key custody and the ability to rotate transparently.

The tenant-domain pattern is unambiguously the correct architecture for any sender that intends to deploy DMARC at p=quarantine or p=reject. Senders who select a third-party platform without verifying that it supports CNAME-delegated signing under the tenant's domain — and who do not audit which signing domain appears in the actual DKIM-Signature: headers of outbound mail — are committing to an alignment failure mode that will surface only on DMARC escalation, typically months later, typically during a high-volume campaign.

The envelope-vs-DKIM gotcha

DKIM signs message headers and body. It does not sign the SMTP envelope. The MAIL FROM address used by the sending IP during the SMTP transaction, and the RCPT TO address that names the recipient, are envelope-level constructs that DKIM does not touch.

This produces a recurring source of operator confusion. DMARC alignment can pass via DKIM — the signing domain in d= aligns with the visible From: — while SPF alignment fails because the envelope MAIL FROM is a third-party platform's bounce-tracking domain that does not align. And the inverse: SPF can align via the envelope while DKIM verification fails because a forwarder mutated the body and broke the body hash.

DMARC requires only one of the two to align; either failure mode is survivable in isolation. The deployment failure to watch for is when both fail simultaneously on legitimate mail — typically because the operator changed one configuration believing it had no effect on the other. Reading the aggregate reports (Chapter 3) is the only reliable way to observe this happening before a campaign produces measurable damage.

Key rotation — the dual-publish-then-drain pattern

The correct procedure for rotating a DKIM key is straightforward and consistently violated under deadline pressure. The pattern, with named selectors for clarity:

  1. Generate the new keypair. Publish the new public key at the new selector — for example, s2._domainkey.example.com — and verify the TXT record resolves from at least three independent DNS resolvers before proceeding.
  2. Lower the TTL on the old selector record to 300 seconds at least 48 hours before the cutover, so receivers' caches turn over quickly when the old record is eventually withdrawn.
  3. Reconfigure the signing system to sign with the new selector. Send a test message and verify the new signature validates at an external diagnostic receiver before signing live traffic.
  4. Leave the old selector's public key published. Continue to monitor verification telemetry for any legitimate mail still arriving signed under the old selector — replay attacks, queued mail from the prior signing configuration, or third-party systems caching the old selector configuration.
  5. After a drain period of at least seven days, and only after observing zero legitimate verification activity under the old selector, withdraw the old TXT record.

The production failure mode is the operator who skips step four entirely — rotates by overwriting the existing selector record in-place — and discovers, hours later, that messages signed before the rotation but still in flight at the receiver fail verification, and that queued mail from the prior signing configuration is now unverifiable. The damage is bounded but observable, and entirely avoidable under the dual-publish pattern.

Common deployment failures observed in production

  • Publishing the private key by mistake. The operator copies the wrong file from the key generation output, publishes the private key in DNS, and discovers the error only when an external scanner flags the record. The fix is rotation; the damage is reputational and proportional to how long the record was live.
  • Leaving t=y in the production record. The test-mode flag was set during initial cutover and never removed. Receivers treat the signature as informational only, DMARC alignment via DKIM is effectively disabled, and the operator believes DKIM is enforcing nothing because, in practice, it is enforcing nothing.
  • Rotating without dual-publish. Described above. The class of operator who reaches for a runbook only after the support tickets aggregate.
  • Signing under simple/simple canonicalization. Verification fails on any message that traverses a whitespace-rewriting forwarder. The failure rate is invisible in steady-state telemetry and surfaces only when DMARC at p=quarantine exposes it.
  • Over-specifying the h= tag. The signer includes transient headers in the signed set, and any intermediate handler that touches those headers breaks the signature. Verification fails at corporate mail gateways disproportionately, with a profile that looks like a tenant-specific deliverability problem.
  • Third-party signing without CNAME delegation. The sequencing platform signs under its own domain, DMARC alignment via DKIM fails, and the sender's DMARC enforcement depends entirely on an SPF configuration nobody audited.
  • 1024-bit RSA on a new selector in 2026. Not failing verification, but sitting at the long tail of the deployment distribution and increasingly flagged by postmaster diagnostics. There is no remaining operational reason to provision a new selector at 1024-bit.

Pre-deployment checklist

  • Public key generated as 2048-bit RSA at minimum, with an Ed25519 companion selector if the receiver mix supports it
  • Selector record resolves from at least three independent DNS resolvers before the signing system is configured to use it
  • Canonicalization set to relaxed/relaxed unless a specific threat model justifies otherwise
  • Signed header set covers from, to, cc, subject, date, message-id, reply-to, mime-version and nothing transient
  • No t=y in the production record
  • For third-party signing arrangements: CNAME delegation under the tenant domain confirmed, and the actual d= value in outbound headers verified to match the tenant domain
  • Rotation runbook documented, with named selectors and a stated drain period of at least seven days
  • External diagnostic verification of a representative outbound message before the signing change goes live

Where DKIM fits in the broader infrastructure

DKIM is the cryptographic anchor for the sender's identity claim. SPF (Chapter 1) tells the receiver which IPs are authorized to send envelope mail under the sending domain; DKIM tells the receiver that the message itself, body and headers, was signed by an entity holding the private key for the named selector. DMARC (Chapter 3) is the policy layer that compares either authenticated identity against the visible From: and decides what to do when neither aligns.

For a cold-sending estate, DKIM is the protocol with the longest tail of post-deployment failure modes. SPF either resolves under the lookup limit or it does not; DMARC either escalates cleanly or produces visible incident traffic. DKIM produces verification failures that are silent at the sender, observable only in aggregate reports, and frequently misdiagnosed as content-classifier issues until the operator parses the actual report XML and discovers that 8% of outbound mail has been failing the body hash for the prior six weeks. The discipline DKIM rewards is operational: rotation hygiene, canonicalization choice, and the willingness to audit third-party signing arrangements before they fragment alignment in production.

Skip the setup

Allston Labs operates the full sending estate as a service.

We provision domains, configure the entire authentication record set, run warmup, and monitor reputation across providers. The stack lives under your entity. The engineer on call lives in your Slack.