A tag-by-tag reference for the DKIM-Signature header defined in RFC 6376 and RFC 8463. Every field explained, every permitted value documented, with worked examples showing the difference between a minimal signature and a production-grade one. Use this page when you need to read, audit or debug a DKIM signature in a received message.
a= algorithm tagc= canonicalisation tagd= and s= tagsh= signed headers tagbh= body hash tagb= signature tagt= and x= timestamp tagsA DKIM-Signature header is a single logical header split across multiple physical lines for readability. Tags are separated by semicolons; whitespace is tolerated around separators. A typical production signature:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=firm.co.uk;
s=2026q2; t=1712448000;
h=from:to:subject:date:message-id:content-type:mime-version;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ
IEhY+sHo+QDSLLzc...The tags can appear in any order, though convention places v= first and b= (the actual signature) last.
| Tag | Purpose | Notes |
|---|---|---|
v= | Version | Must be 1. The only defined version. |
a= | Algorithm | Signature algorithm — typically rsa-sha256 or ed25519-sha256. |
d= | Signing domain | The domain that claims responsibility for the signature. |
s= | Selector | Identifies which key was used. Combined with d= to find the DNS record. |
h= | Header list | Colon-separated list of header field names that are signed. Must include from. |
bh= | Body hash | Base64-encoded hash of the canonicalised body. |
b= | Signature | Base64-encoded signature over the signed headers and body hash. |
A signature missing any of these tags is invalid and must be ignored by receivers.
| Tag | Purpose | Default if absent |
|---|---|---|
c= | Canonicalisation | simple/simple |
t= | Signing timestamp | Not present — no timestamp |
x= | Expiry timestamp | Never expires |
i= | Agent or user identifier | Derived from d= |
q= | Query method | dns/txt |
l= | Body length limit | Entire body signed |
z= | Copied original headers | Not present |
Best practice: explicitly set c= (use relaxed/relaxed) and t= (Unix timestamp at signing time). Omit i=, q=, x=, l= and z= unless you have a specific reason to use them. In particular, do not use l= — it is a known security footgun that permits content injection below the signed region.
a= algorithm tagDefines the cryptographic algorithms used for signing. The tag value is in the form algo-hash:
| Value | Algorithm | Hash | Status |
|---|---|---|---|
rsa-sha256 | RSA | SHA-256 | Current standard |
ed25519-sha256 | Ed25519 (RFC 8463) | SHA-256 | Modern alternative |
rsa-sha1 | RSA | SHA-1 | Deprecated — do not use |
RFC 8301 explicitly deprecated rsa-sha1 in 2018 owing to SHA-1 collision weaknesses. Any modern UK deployment should sign with rsa-sha256 and optionally also ed25519-sha256. If you encounter a received message signed with rsa-sha1, treat it as equivalent to unauthenticated for security purposes.
Key sizes: use RSA-2048 or larger. RSA-1024 is marginal in 2026 and explicitly discouraged by NCSC guidance. Ed25519 keys are fixed at 256 bits of effective security, equivalent to roughly RSA-3072.
c= canonicalisation tagDefines how headers and body are normalised before hashing. Format: header-algo/body-algo. Permitted values for each:
simple — no normalisation; the signed content must be preserved byte-for-byte.relaxed — whitespace normalised, header names lower-cased, trailing empty lines removed.The four permitted combinations:
| Value | Notes |
|---|---|
simple/simple | Strictest; breaks on most forwarders. The default if c= is omitted. |
simple/relaxed | Uncommon. |
relaxed/simple | Uncommon. |
relaxed/relaxed | Modern standard. Tolerant of routine transport transformations. |
Use relaxed/relaxed in production unless you have a specific reason not to.
d= and s= tagsTogether these identify which public key to fetch. The DNS lookup the receiver performs is TXT {s}._domainkey.{d}.
d= (Signing Domain Identifier, SDID) is any domain the signer controls. For DMARC alignment to pass via DKIM, the d= must match (relaxed or strict alignment, depending on the DMARC policy) the domain in the From: header. In practice:
From: is [email protected], d=firm.co.uk aligns exactly.From: is [email protected], the alignment depends on mode: under relaxed, d=firm.co.uk still aligns (shared organisational domain); under strict, it does not.d=mcsv.net but From: is [email protected], no alignment — DMARC does not pass via this signature.s= (selector) is the label you chose when deploying DKIM. It has no meaning beyond naming a specific key. Common patterns include 2026q1, mail, marketing, and provider-specific names like selector1 (Microsoft 365).
h= signed headers tagA colon-separated list of header field names. Each name is the name of a header that was present in the message at signing time and is included in the hash computed to produce the signature.
RFC 6376 Section 5.4 requires From to be included. Everything else is at the signer's discretion. A conservative modern selection:
h=from:to:subject:date:message-id:content-type:mime-versionA longer selection for more sensitive applications:
h=from:to:cc:subject:date:message-id:content-type:mime-version:reply-to:in-reply-to:referencesA header name can be listed twice. This technique — "over-signing" — requires that the header appear the same number of times in the message or the signature becomes invalid. It defends against header addition attacks where an attacker prepends a second From: or Subject: header to spoof display.
A name can be in the h= list even if the header is not present in the message. In canonicalisation, an absent header contributes an empty string. This is another defence against attackers who might later inject the named header.
bh= body hash tagThe base64-encoded hash of the canonicalised body. The hash algorithm is the same as the signature algorithm's hash (SHA-256 in practice). The computation:
c=.l= length if that tag is present (usually not).A typical SHA-256 body hash is 43 base64 characters followed by a single = padding:
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=If the body hash in the signature does not match the receiver's recomputation, the signature fails — the body has been modified.
b= signature tagThe actual signature. Base64-encoded. Computed over:
h=) in the order they appear in h=.b= value removed (replaced with an empty string).At verification time, the receiver performs the same steps using the public key. If the computed signature matches the presented b=, the signature is valid.
RSA-2048 signatures are 256 bytes (344 base64 characters). Ed25519 signatures are 64 bytes (88 base64 characters).
t= and x= timestamp tagst= is the Unix timestamp at signing time. Include it — receivers use it for freshness checks and some spam filters weight recent timestamps more favourably. The cost is zero.
x= is the Unix timestamp at which the signature expires. Omit it for most use cases; expired signatures are treated as unsigned by receivers, which is rarely the desired behaviour. Use x= only for specific applications like one-time-use receipts where you deliberately want the signature to become invalid after a window.
DKIM-Signature: v=1; a=rsa-sha256; d=firm.co.uk; s=mail;
h=from; bh=xyz...=; b=abc...Required tags only. Canonicalisation defaults to simple/simple (brittle). Body hash and signature are abbreviated for illustration.
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=firm.co.uk;
s=2026q2; t=1712448000;
h=from:to:subject:date:message-id:content-type:mime-version;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ
IEhY+sHo+QDSLLzc/JHhMMH9jxStaflXekkpu9gH58iv87zgQBKc
exo67lGNO2JNn5nOb0CcsyvJlbjC/X1Qfec7hSqGEI/AbZK0+QrV
1lwWVYnLDjQuHpMRnC3MjtIlFfSS8NcI=All recommended tags present. Relaxed canonicalisation. Standard seven-header signing set. Timestamp included.
DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=firm.co.uk;
s=2026q2-ed; t=1712448000;
h=from:to:subject:date:message-id:content-type:mime-version;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=sG2a/8fAx1kxLIhbqfNBzoA6CemNVkwdS9HCbbWLX8W...Much shorter b= value because Ed25519 signatures are 88 base64 characters vs RSA-2048's 344.
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=firm.co.uk;
s=2026q2; t=1712448000;
h=from:from:subject:subject:date:message-id:content-type:mime-version;
bh=...; b=...from and subject each appear twice — any attempt to add a second header of those types would invalidate the signature.
When diagnosing a received message, follow this reading order:
v=1. Confirm this is DKIM. No other version exists.a=. Check the algorithm. rsa-sha256 or ed25519-sha256 is good; rsa-sha1 is a red flag.d=. Note the signing domain. This is what aligns (or fails to align) with the From: header for DMARC.s=. Note the selector. Combined with d=, this tells you where the public key is published.h=. Scan the signed header list. Does it include From? (It must.) Does it include Subject? (Usually yes.) Is anything unexpected included or absent?c=. Check canonicalisation. relaxed/relaxed is standard. simple/simple with a failing signature on a forwarded message is a common pattern — the signature is failing because the forwarder made a minor change the simple canonicalisation cannot tolerate.Authentication-Results. Does the receiver report dkim=pass with the same selector and domain? If yes, the signature verifies. If dkim=fail, something in the chain has broken — commonly body modification or DNS misconfiguration.a=rsa-sha256, c=relaxed/relaxed, your domain in d=, a quarterly-rotating selector in s=, and the standard seven-header signing set. Production signatures additionally carry timestamps. This configuration is chosen for maximum forwarding resilience and NCSC guidance compliance.
Understanding why c=relaxed/relaxed is the practical default is easier with a concrete example. Consider a simple message:
From: [email protected]
Subject: Invoice #2026-04-21
Date: Tue, 21 Apr 2026 10:00:00 +0100
Dear Customer,
Your invoice is attached.
Kind regards,
Accounts Team
Several things are non-normal in this message: multiple consecutive spaces in the Subject line, a whitespace run in the body ("Your invoice is attached."), trailing empty lines.
simple/simpleThe message must be preserved byte-for-byte. A forwarder that normalises the double spaces, trims trailing lines, or re-wraps long lines will break the signature.
relaxed/relaxedNormalisation rules per RFC 6376 Section 3.4:
Result after canonicalisation:
from:[email protected]
subject:Invoice #2026-04-21
date:Tue, 21 Apr 2026 10:00:00 +0100
Dear Customer,
Your invoice is attached.
Kind regards,
Accounts Team
A forwarder that modifies whitespace in ways the sender already normalised does not break the signature. This is what "relaxed" buys you and why it is the modern default.
Signatures without bh= are malformed and always fail verification. Some ancient signers emit them erroneously; treat as broken.
A signature with d=sender.com and a message From: [email protected] will pass DKIM verification (the signature is mathematically valid) but fails DMARC alignment. This is the common pattern when a third-party service signs on its own behalf rather than on yours — the fix is custom sending domain authentication.
l= setIf you see l=1024 or similar, treat the signature cautiously. An attacker can append arbitrary content after byte 1024 without breaking the signature. Modern senders should never use l=.
A signature with a=rsa-sha1 in 2026 is deprecated. Receivers may accept it for backward compatibility but UK NCSC guidance treats it as inadequate. Upgrade the signer.
The DKIM record will have k=rsa and a short p= value. Check the public key modulus length — if 1024 bits, rotate to 2048 as a priority.
d= and s=A message should not be signed twice with the same key. Two signatures with identical d= and s= suggests a misconfiguration — commonly, a mail server that signs and then relays through another server that signs again with the same key.
DKIM-Signature's structure has been remarkably stable. RFC 4871 (2007) defined the original format; RFC 6376 (2011) refined without breaking; RFC 8463 (2018) added Ed25519 as a new algorithm value; RFC 8301 (2018) deprecated SHA-1 hash usage. No major format change is expected in the foreseeable future.
What has changed over time:
rsa-sha1 was acceptable. By 2020 it was deprecated. In 2026 you should see only rsa-sha256 and ed25519-sha256 in modern signatures.simple/simple was common in early deployments; relaxed/relaxed is the modern default because it survives realistic mail transport.default forever; modern deployments rotate quarterly with date-based selectors.Q: Can a single message carry multiple DKIM-Signature headers?
A: Yes. Each is verified independently. For DMARC alignment, only one needs to align with the From: domain. This is how third-party signers coexist with your main platform's signature.
Q: What happens if the h= list includes a header that is not in the message?
A: The canonicalised header is empty for that name, and the signature still verifies (assuming other content is unchanged). This is a defensive technique against later header injection.
Q: Does the order of tags in the DKIM-Signature header matter?
A: No. Tag order is free. Convention places v= first and b= last for readability. The verifier reassembles tags by name, not position.
Q: Can I see the private key from looking at the DKIM-Signature header?
A: No. The signature header exposes only the public information — signing domain, selector, algorithm, signed header list, body hash, and the signature itself. The private key never appears in the message.
Q: What is the maximum length of a DKIM-Signature header?
A: RFC 5322 limits header lines to 998 characters but permits continuation via soft line breaks. In practice DKIM-Signature headers are around 400-600 characters for RSA-2048 and 200-300 characters for Ed25519.
Q: What is the z= tag used for in practice?
A: It copies the original header values at signing time, so a verifier can inspect whether specific headers have been modified between signing and arrival. Rarely used; tools that produce diagnostic output sometimes include it.
Q: Do I need to understand this header to use DKIM on my domain?
A: No — for most UK business users, DKIM just works through the hosting platform. Reading the header becomes useful only when diagnosing a failure (typically with a specific receiver or forwarder) where "dkim=fail" appears in the Authentication-Results.
Q: Why does Gmail sometimes show dkim=neutral?
A: It means verification was performed but the result could not be categorised as clear pass or clear fail. Typically indicates a configuration issue such as a DNS lookup timeout or a malformed DKIM record.
Q: Can an attacker strip the DKIM-Signature header in transit?
A: Yes — intermediate servers can remove headers. Without the signature the message is unauthenticated, which under DMARC p=reject causes the message to be rejected. This is a form of integrity failure: the message as delivered does not match what the sender authenticated.
Q: Are there UK-specific validation tools for checking a signature?
A: Most general tools (MXToolbox, Hardenize, Port25 verifier) work from any jurisdiction. NCSC Mail Check provides UK public-sector-focused validation. Mail Hardener's UK offering provides deeper signature diagnostic detail suitable for enterprise DMARC teams.
Q: If I see two DKIM-Signature headers with different d= domains, which one wins?
A: Neither "wins" exclusively — each is verified independently. For DMARC purposes, the one whose d= aligns with the From: header is what counts. Both can be valid; only alignment to From: decides DMARC.
Q: Can I sign just the headers without hashing the body?
A: No. RFC 6376 requires a body hash (bh=) in every valid signature. Body signing is the core of DKIM — verifying the body was not modified in transit.
Q: What tools show a DKIM-Signature in a readable format?
A: The Authentication-Results header in the delivered message summarises the outcome. For the raw signature, use View Source in your mail client. For deep diagnostic, pipe the message through the opendkim-testmsg tool or similar command-line validator.
Q: Is the DKIM-Signature header visible to ordinary email users?
A: Only if they deliberately choose "View source" or equivalent. Default mail client displays hide all authentication headers. For most UK users, DKIM operates invisibly.
Q: Do I need to understand bh= to troubleshoot DKIM failures?
A: Not usually. When DKIM fails, the receiver's Authentication-Results header typically tells you why (body hash did not verify is the common failure mode on forwarded or modified messages). The bh= tag itself is not the part you need to inspect — the receiver has already compared it.
Q: What is the practical risk of using the l= body-length limit?
A: If l=512 is set, the signer only hashes the first 512 bytes of the body. An attacker can append arbitrary malicious content after byte 512 without breaking the signature. The appended content is often a phishing call-to-action or a fake footer. Never use l= in production signatures.
Q: Does the DKIM-Signature header affect message routing?
A: No. Routing is determined by MX and SMTP-level headers; DKIM-Signature is only inspected at verification time by the receiving server. It does not change how the message travels.
Q: My mail server only supports c=simple/simple. Should I upgrade?
A: Yes, urgently. Simple canonicalisation breaks on most forwarders and fails whenever mail servers normalise whitespace or re-wrap long lines. Modern mail servers (Postfix with OpenDKIM, Exim with DKIM support, Axigen, Exchange) all support relaxed canonicalisation. If your MTA does not, migrate to a modern one.
Q: Is there a best practice for which headers to include in h=?
A: At minimum: from. Recommended: from, date, subject, message-id, content-type, mime-version, to. For high-assurance deployments you can additionally include reply-to, references, in-reply-to. Do not sign headers that forwarders commonly modify (for example received) — they will break the signature on any non-trivial transport path.