The DKIM-Signature header: every tag decoded
The DKIM-Signature header carries the signature and everything needed to verify it: d, s, h, bh, b and more. What each tag means and which choices matter.
Everything a receiver needs to verify a DKIM signature travels in one header: DKIM-Signature. It names the signing domain and key, lists exactly which headers were signed, carries a hash of the body, and ends with the signature itself. Reading this header fluently turns DKIM from a black box into something you can debug from any delivered message.
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=example.com; s=s2026a; t=1752504000;
h=from:from:to:subject:subject:date:mime-version:content-type;
bh=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN/XKdLCPjaYaY=;
b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ...The identity tags: d, s, i
d= names the signing domain and s= the selector; together they tell the receiver where to fetch the public key. d= is also the domain that DMARC alignment compares against the From: header, which makes it the single most consequential value in the header: a signature with your ESP's platform domain in d= verifies perfectly and does nothing for your DMARC. The optional i= tag identifies the signing agent (a user or subdomain under d=) and rarely needs attention.
The algorithm tags: a and c
a= declares the signing algorithm. rsa-sha256 is the production standard; rsa-sha1 is obsolete and refused by modern verifiers, and ed25519-sha256 exists in the spec with limited receiver support. c= sets the canonicalization modes for header and body, in that order: relaxed/relaxed tolerates the whitespace and wrapping changes that mail servers routinely make, while simple breaks on nearly any change. relaxed/relaxed is the correct default for surviving real-world transit.
The content tags: h, bh, b
h= lists the headers covered by the signature, in signing order. From must be included by specification; To, Subject, Date, and the MIME structure headers belong there in practice, since an unsigned header can be altered or added without breaking the signature. bh= is the hash of the canonicalized body, computed before signing. b= is the signature itself: the private-key operation over the canonicalized headers, including a reconstruction of the DKIM-Signature header with b= empty.
The lifetime tags: t and x
t= timestamps the signature; x= sets an expiry after which verifiers treat it as invalid. Expiry bounds how long a captured message can be replayed, at the cost of failing verification on slow delivery paths and late forwards, a trade-off weighed in the security hardening entry. Most senders leave x= unset and bound replay through key rotation instead.
The tag to never use: l
l= limits the body hash to the first N bytes, allowing anything appended beyond that point to ride along without invalidating the signature. It existed for a mailing list edge case and became an attack surface: 2024 research demonstrated practical content injection against senders using it, and current guidance from providers and the security community is unambiguous. Do not set l=, and treat its presence in any signature you audit as a finding.
Tag reference
| Feature | Required | Meaning |
|---|---|---|
| v=1 | ✓ | DKIM version |
| a=rsa-sha256 | ✓ | Signing algorithm |
| d= / s= | ✓ | Signing domain and selector: where the key lives, what DMARC aligns |
| h= | ✓ | Signed headers, From mandatory; repeat entries to oversign |
| bh= / b= | ✓ | Body hash and the signature value |
| c= | no (simple/simple default) | Canonicalization; use relaxed/relaxed |
| t= / x= | ✕ | Timestamp and optional expiry |
| l= | never use | Partial body coverage; a demonstrated attack surface |
Frequently asked questions
Which headers should h= cover?
Why does b= wrap across multiple lines?
What does dkim=fail with body hash mismatch mean?
Can I inspect a signature without any tools?
Key takeaways
- The DKIM-Signature header is self-describing: d= and s= locate the key, h= lists what is signed, bh= and b= carry the proof
- d= is the domain DMARC alignment tests, which makes it the most consequential tag in the header
- Use rsa-sha256 with relaxed/relaxed canonicalization as the production baseline
- Oversign mutable headers by listing them once more than they appear in h=
- Never set l=; partial body coverage is a demonstrated content-injection vector