You know that DKIM signs your emails. But what exactly is inside that DKIM-Signature header? Let’s break down every field and follow the verification process from start to finish.
A Real DKIM Header, Dissected
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=example.com; s=selector2024;
h=from:to:subject:date:mime-version:content-type;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
t=1714123456;
b=dHJ5aW5nIHRvIHJlYWQgdGhpcyBzaWduYXR1cmU/IE5pY2Ug
dHJ5IDspIFRoaXMgaXMganVzdCBhIGR1bW15IGV4YW1wbGU=
Every Field Explained
v=1: Version
The DKIM protocol version. Only one version exists (1), defined by RFC 6376. This field is mandatory.
a=rsa-sha256: Algorithm
Two elements combined:
- Cryptosystem:
rsa(most common) ored25519(emerging, RFC 8463) - Hash function:
sha256(standard) orsha1(obsolete and vulnerable, do not use)
| Combination | Status |
|---|---|
rsa-sha256 | Standard, recommended |
rsa-sha1 | Obsolete, some providers reject it |
ed25519-sha256 | Future standard, partial support |
c=relaxed/relaxed: Canonicalization
Defines how content is normalized before computing the signature. The format is headers/body:
Simple mode:
- Headers: no modification (case-sensitive, whitespace-sensitive)
- Body: removes duplicate empty lines at the end of the message
Relaxed mode:
- Headers: converts to lowercase, reduces multiple spaces, removes trailing whitespace
- Body: same + reduces multiple spaces on each line
In practice, relaxed/relaxed is the standard choice because it tolerates minor changes made by relay servers (added spaces, case changes).
d=example.com: Signing Domain
The domain responsible for the signature. This domain must align with the From: header for DMARC to validate DKIM alignment.
The public key will be looked up in this domain’s DNS zone.
s=selector2024: Selector
Identifies which public key to use among those published for the domain. The full DNS record will be:
selector2024._domainkey.example.com TXT "v=DKIM1; k=rsa; p=MIIBIjANBg..."
Selectors enable key rotation without downtime: publish a new key on a new selector before switching.
h=from:to:subject:date:...: Signed Headers
The list of headers included in the signature. Only From: is technically mandatory, but in practice you always include:
from,to,subject,date(basic integrity)mime-version,content-type(message structure)message-id(uniqueness)reply-to,cc(if present)
A header not listed can be modified in transit without breaking the signature. A listed header that changes will invalidate the signature.
bh=2jUSOH9N...: Body Hash
The hash of the canonicalized message body, encoded in Base64. Computed using the hash algorithm defined in a= (sha256).
This field allows verifying that the body hasn’t been modified independently of the full signature.
t=1714123456: Timestamp
The Unix timestamp of when the signature was created. Allows the receiving server to verify the signature isn’t too old (some providers reject signatures older than a few days).
b=dHJ5aW5n...: The Signature
The final result: the RSA (or Ed25519) signature of the canonicalized headers + body hash, encoded in Base64. This value is decrypted with the public key for verification.
The Verification Process, Step by Step
When a receiving server gets an email with a DKIM signature:
1. Format Validation
The server checks that all mandatory fields are present (v, a, b, bh, d, h, s).
2. Body Canonicalization
The body is normalized according to the algorithm defined in c= (right side).
3. Body Hash Computation and Comparison
The hash of the canonicalized body is computed with sha256 and compared to bh=.
- If mismatch →
body hash did not verify→ DKIM fail
4. Public Key Retrieval
The server makes a DNS query for s=._domainkey.d= and retrieves the TXT record containing the public key.
5. Header Canonicalization
The headers listed in h= are canonicalized according to the algorithm defined in c= (left side).
6. Signature Verification
The signature b= is decrypted with the public key. The result is compared with a computed hash of the canonicalized headers + body hash.
- If match → DKIM pass
- If mismatch →
signature did not verify→ DKIM fail
Optional Fields
| Field | Description |
|---|---|
x= | Signature expiration (Unix timestamp) |
i= | Identity of the signing agent (e.g., user@example.com) |
l= | Number of body bytes included in the hash (dangerous, discouraged) |
q=dns/txt | Key retrieval method (only dns/txt exists) |
z= | Copy of original headers (for diagnostics) |
Warning: the
l=field is a known vulnerability. If present, an attacker can append content to the message body without breaking the signature. Never use it.
How to Analyze a DKIM Signature
- Header Analyzer: paste raw headers for a visual analysis of each DKIM field
- DKIM Checker: verify the published DNS record for your selector
- Free audit: send a test and see the full result
Further Reading
- Configure DKIM, the practical guide
- RSA Key Size: 1024 vs 2048 vs Ed25519
- Why Two DKIM Signatures?
- RFC 6376, the complete DKIM specification