DKIM (DomainKeys Identified Mail, RFC 6376) adds a signature header to each email. The sending server signs chosen headers and the body with a private key. The receiver fetches the matching public key from DNS and checks the signature. A pass proves the signing domain approved the message and that it was not changed on the way.
DKIM is the half of email authentication that survives forwarding, which makes it the half DMARC depends on most. This guide covers how it works, what each tag means, how selectors look at Google and Microsoft 365, and which key sizes are acceptable in 2026.
How DKIM signing and verification work
Signing (outbound):
- The sending server canonicalizes the body (normalizes whitespace and line endings) and hashes it with SHA-256. That hash goes in
bh=. - It picks which header fields to sign and lists them in
h=.Frommust be one of them. - It hashes those headers plus the DKIM-Signature header itself (with
b=empty) and signs that hash with the private key. The result goes inb=. - It adds the finished
DKIM-Signature:header to the message.
Verification (inbound):
- The receiver reads
d=ands=from the signature. - It queries the TXT record at
<s>._domainkey.<d>for the public key. - It recomputes the body hash and compares it to
bh=. If they differ, the body changed: fail. - It recomputes the header hash and checks
b=against it with the public key. If that matches: pass.
So a signature breaks if anything touches a signed header or the body after step 4. In our experience the culprit is rarely an attacker. It is a mailing list appending an unsubscribe footer, or a gateway rewriting links for click scanning, or a list server turning Subject: Invoice into Subject: [finance] Invoice.
The DKIM-Signature header, tag by tag
This signature comes from the worked example in RFC 8463, Appendix A:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=football.example.com; i=@football.example.com;
q=dns/txt; s=test; t=1528637909; h=from : to : subject :
date : message-id : from : subject : date;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=F45dVWDfMbQDGHJFlXUNB2HKfbCeLRyhDXgFpEL8GwpsRe0IeIixNTe3...
| Tag | Meaning | In the example |
|---|---|---|
d= |
Signing domain. DMARC compares this to the From domain. | football.example.com |
s= |
Selector. Tells the receiver which key record to fetch. | test |
h= |
Signed header fields, in order. | from, to, subject, date, message-id |
bh= |
Base64 SHA-256 hash of the canonicalized body. | 2jUSOH9N... |
b= |
The signature itself. | F45dVWDf... |
a= |
Algorithm. rsa-sha256 or ed25519-sha256. |
rsa-sha256 |
c= |
Canonicalization for header/body: simple or relaxed. |
relaxed/relaxed |
t= |
Signing time, Unix seconds. | 1528637909 |
Notice that from, subject and date appear twice in h=. Listing a header one more time than it occurs is a known defence: it signs the absence of a second copy, so nobody can add another From: header without breaking the signature.
Use c=relaxed/relaxed unless you have a reason not to. simple breaks on small whitespace changes that many relays make.
Selectors: where the public key lives
The key record sits at <selector>._domainkey.<domain>. One domain can have many selectors, one per sending service, and each service holds its own private key. You cannot list a domain's selectors from DNS. You find them in the s= tag of real messages or in each vendor's setup page.
Google Workspace: "google"
Google's Set up DKIM page says: "The default prefix selector is google." So a Workspace domain publishes a TXT record at google._domainkey.example.com. On 19 October 2026:
$ dig +short TXT google._domainkey.github.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj6T5sl/RwdSq..."
Microsoft 365: selector1 and selector2 as CNAMEs
Microsoft 365 does not ask you to publish a key. It asks for two CNAME records that point into Microsoft's DNS, where Microsoft holds and rotates the keys. Microsoft's DKIM page gives the older format as selector1-contoso-com._domainkey.contoso.onmicrosoft.com. New custom domains added since May 2025 use a newer target ending in dkim.mail.microsoft. The page also says "Only one selector is active" at a time; the other waits for the next rotation.
You can see both halves on microsoft.com itself:
$ dig +noall +answer TXT selector1._domainkey.microsoft.com
selector1._domainkey.microsoft.com. CNAME selector1-microsoft-com._domainkey.microsoft.onmicrosoft.com.
$ dig +noall +answer TXT selector2._domainkey.microsoft.com
selector2._domainkey.microsoft.com. CNAME selector2-microsoft-com._domainkey.microsoft.onmicrosoft.com.
selector2-microsoft-com._domainkey.microsoft.onmicrosoft.com. TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3..."
On that day the selector1 target returned NXDOMAIN and selector2 returned a key. That is the rotation design working: one live, one idle.
Several services on one domain
github.com shows how selectors keep services apart. On 19 October 2026 it had google (Google Workspace), selector1 (a CNAME to Microsoft 365), s1 (a CNAME to SendGrid) and pf2023 (a TXT key) all live at once. Each sender signs with its own key and never needs the others.
Key sizes: what the RFCs and mailbox providers require
RFC 8301 updated DKIM's crypto rules in 2018:
- Signers MUST use RSA keys of at least 1024 bits and SHOULD use at least 2048.
- Verifiers MUST NOT treat signatures from keys under 1024 bits as valid.
rsa-sha1MUST NOT be used for signing or verifying.
The mailbox providers say the same. Gmail's sender guidelines state that "Sending to personal Gmail accounts requires a DKIM key of 1024 bits or longer" and recommend 2048. Yahoo's Sender Hub asks for "a minimum 1024-bit key length".
Real keys still vary. We decoded the public keys published on 19 October 2026:
| Record | Key size |
|---|---|
google._domainkey.github.com |
2048-bit RSA |
google._domainkey.atlassian.com |
2048-bit RSA |
A large payments company's google selector |
1024-bit RSA |
| A large software company's Microsoft 365 selector | 1024-bit RSA |
You can tell without decoding. A base64 RSA key starting MIGfMA0 is 1024-bit. One starting MIIBIjAN is 2048-bit.
A 2048-bit key is about 400 characters of base64, longer than one 255-character TXT string. Most DNS panels split it into two strings for you. If yours does not, split it yourself into two quoted strings with no space at the join.
Ed25519 signatures (RFC 8463)
RFC 8463 added a=ed25519-sha256. Its keys are 256 bits, and the base64 p= value is only 44 characters, so it fits in one TXT string. The key record uses k=ed25519.
The RFC says "Signers SHOULD implement and verifiers MUST implement" it. In practice not every receiver verifies Ed25519 yet, so the RFC's own transition advice is to sign twice: one RSA signature and one Ed25519 signature, with different selectors and the same d=. A receiver that cannot check Ed25519 ignores that signature and uses the RSA one.
Key rotation
Rotate by selector, never by overwriting a live key.
- Generate a new key pair and publish the public key under a new selector (for example
2026q4). - Wait for DNS caches to expire (the record's TTL, often an hour or a day).
- Switch the signer to the new selector.
- Keep the old record for a few days so messages still in transit verify.
- Revoke the old selector by publishing it with an empty key:
v=DKIM1; p=.
RFC 6376 section 3.6.1 defines that last step: an empty p= "means that this public key has been revoked". gmail.com does exactly this. On 19 October 2026 its old selectors 20161025, 20210112, 20221208 and 20230601 all returned p= with no key.
Rotate when staff with key access leave, when you change vendors, and on a fixed schedule you write down. Microsoft 365 and Google Workspace hold the private keys themselves, so for them rotation is a button in the admin console.
DKIM alignment for DMARC
A DKIM pass helps DMARC only when the d= domain aligns with the domain in the visible From header (RFC 7489 section 3.1.1).
- Relaxed (
adkim=r, the default): the organizational domains must match.d=mail.example.comaligns withFrom: news@example.com. - Strict (
adkim=s): the domains must be identical.
The common failure: an email platform signs with its own domain until you finish its custom domain setup. The signature passes, but d= is the vendor's domain, so DMARC sees no aligned DKIM. Check d= on a real message from every service on your integrations list, for example Google Workspace or Microsoft 365.
Aligned DKIM matters more than aligned SPF because it survives forwarding. A forwarded message arrives from the forwarder's IP and fails SPF. If nothing in the path edited it, its DKIM signature still verifies, and DMARC passes on that alone.
Testing DKIM
Three checks catch nearly every problem:
- Key published? Enter the domain and selector in the DKIM checker, or run
dig +short TXT <selector>._domainkey.<domain>. Look for a non-emptyp=,k=rsaork=ed25519, and no stray quotes or spaces inside the key. - Message signed? Send a test to a Gmail address, open "Show original" and look for
dkim=pass header.i=@<your domain>in the Authentication-Results header. If the domain after@belongs to a vendor, alignment is not set up. - Aligned in practice? DMARC aggregate reports list DKIM results and the
d=domain for every source sending as you. Upload a report to the DMARC analyzer to see which sources fail.
DMARC+ adds DKIM validation to the same report view, so a selector that stops verifying shows up next to the source that uses it.
Next step
List every service that sends mail as your domain. For each one, record its selector, key size and d= domain in a table. Any row with a 1024-bit key, a vendor d= or a blank selector is your work queue. Start with the one that sends the most mail.