DKIM Selectors: How Key Lookup and Rotation Work
A DKIM selector is the s= value in a DKIM-Signature header. A receiver combines it with the signing domain in d= to look up the public key at selector._domainkey.domain. Selectors let one domain publish several keys at once, which is what makes a safe key rotation possible.
For a routine rotation, create a new selector. Publish its public key first, switch every signer to the new private key, keep the old public key available while old messages may still be verified, and retire the old selector only after that overlap. Do not replace the key under the existing selector.
Start from the two tags in the signature
This hypothetical header signs with mail.example.com and uses the selector 2026q3:
DKIM-Signature: v=1; a=rsa-sha256;
d=mail.example.com; s=2026q3;
h=from:to:subject:date; bh=...; b=...
The receiver constructs one DNS name:
2026q3._domainkey.mail.example.com
RFC 6376 defines that namespace. The receiver requests a TXT record and uses its p= value as the public key. A record commonly has this shape:
v=DKIM1; k=rsa; p=MIIBIjANBgkqh...
DNS providers may display a long value as several quoted strings. DKIM joins the TXT strings without inserting spaces. The standard also requires the TXT record to be unique for a selector name. Publishing two competing DKIM records at the same name creates an undefined result, not a rotation.
Derive the lookup name without guessing
I use the received header as the source of truth. The small diagnostic helper below unfolds continuation lines, reads d= and s=, and prints the exact DNS name to inspect:
export function dkimKeyName(header) {
const unfolded = header.replace(/\r?\n[ \t]+/g, " ");
const value = unfolded.replace(/^DKIM-Signature:\s*/i, "");
const tags = Object.fromEntries(
value
.split(";")
.map(part => part.trim().match(/^([a-z])=(.*)$/i))
.filter(Boolean)
.map(match => [match[1].toLowerCase(), match[2].trim()])
);
if (!tags.d || !tags.s) {
throw new Error("DKIM-Signature needs both d= and s=");
}
return `${tags.s}._domainkey.${tags.d}`.toLowerCase();
}
I ran it against the folded hypothetical header above. The test returned:
2026q3._domainkey.mail.example.com
tests 2
pass 2
fail 0
This is a lookup aid, not a DKIM verifier. Cryptographic verification also checks the body hash, signed headers, canonicalization, signature, algorithm, and key constraints.
One selector should keep one key identity
The tempting shortcut is to leave s=default in the signer and overwrite default._domainkey.example.com with a new public key. That creates an ambiguous cutover. Some receivers may still have the old DNS answer cached while new messages already carry signatures made by the new private key.
RFC 6376 is unusually direct here: reusing a selector with a new key makes it impossible to distinguish a message that failed because the old key is gone from a message that has a bad signature. A fresh selector gives each key pair its own DNS identity and lets the old and new public keys coexist.
If you generate RSA keys yourself, RFC 8301 requires at least 1024 bits and recommends at least 2048 bits. It also requires rsa-sha256 rather than rsa-sha1. Your sending platform may manage the algorithm and key material for you, but the selector boundary is still visible in the message and DNS.
Use a four-phase cutover
A rotation is safer when each phase has an observable completion signal. I would record the old selector, new selector, signing domain, owners, and timestamps in the change ticket before touching DNS.
| Phase | Signer state | DNS state | Completion signal |
|---|---|---|---|
| 1. Prepublish | Still signs with old key | Old and new public keys exist at different selectors | The new record resolves correctly from the authoritative path and normal recursive resolvers |
| 2. Switch | Move every sending stream to new key | Keep both records | Fresh received messages show the new s= value and pass DKIM |
| 3. Overlap | New key only | Keep both records | No old-key messages remain in outbound queues, and the chosen validation interval has elapsed |
| 4. Retire | New key only | Remove the old record | Monitoring shows no unexpected old selector and new mail continues to pass |
There is no universal “wait exactly seven days” rule in DKIM. RFC 6376 says to retain the old public key for a reasonable validation interval and notes that a verifier may delay validation. Your interval should cover the way your own mail can queue, be retried, or be processed later. If you cannot justify the interval from those systems, leave more overlap.
Do not retire the old selector on switch day
Changing the signer stops new messages from using the old private key. It does not change messages that are already queued or delivered. Those messages still contain the old s= value and can still require the old public key when a receiver verifies them.
This is why the safe order is publish, switch, observe, retire. The old private key can be removed from active signers after the switch, while its public counterpart stays in DNS for validation. Public keys are not secret.
What if every message you send appears immediately at a test inbox? That proves the new path works. It does not prove that no delayed message exists elsewhere. Use queue state and logs, not one fast delivery, to decide when the overlap is complete.
A compromised key needs a different response
Routine rotation preserves old verification. Compromise response tries to stop trust in the old key as quickly as DNS caching permits. RFC 6376 defines an empty p= value as a revoked key, and verifiers treat signatures using it as failed.
v=DKIM1; p=
Do not follow the routine overlap plan with a known-compromised private key. Stop affected signers, publish a distinct replacement selector, move legitimate traffic, revoke or remove the exposed key according to the incident plan, and expect cached DNS data to delay a globally consistent result.
Verify the message, not only the DNS record
A perfect-looking TXT record can still be disconnected from the active signer. After the switch, inspect a newly received message and confirm all four facts:
- The
d=signing domain is the one you intended. - The
s=selector is the new value. - The derived DNS name returns the matching public key.
- The receiver reports DKIM pass for that signature.
If DMARC matters to the stream, also confirm that the passing DKIM d= domain aligns with the visible From domain. The SPF, DKIM, and DMARC header guide walks through that separate alignment decision.
DKIM proves that a message was signed by a domain with access to the private key and that the signed content survived verification. It does not prove that the sender is safe, that a recipient mailbox exists, or that a person owns an address. Address verification and inbox confirmation solve different problems.
The rotation is finished when new mail signs and verifies with the new selector, the old validation window is genuinely clear, and removing the old public key does not strand messages that still depend on it.