When a message shows DKIM=fail, the DNS record is often checked first.
Plenty of the time, DNS is not the thing that changed.
The message changed.
That is exactly where DKIM canonicalization comes in. It defines how much whitespace or formatting cleanup a verifier is allowed to ignore before deciding the signed message is no longer the same message.
If the term sounds overly abstract, the practical version is simple: DKIM can survive some mail-path cleanup, but it does not survive arbitrary rewriting. Footers, click tracking, MIME re-encoding, and other downstream edits are where DKIM=fail usually starts.
DKIM signs a canonicalized version of selected headers and of the message body. The canonicalization mode is carried in the c= tag inside DKIM-Signature.
Typical examples:
DKIM-Signature: ... c=relaxed/relaxed; ...
DKIM-Signature: ... c=relaxed/simple; ...
DKIM-Signature: ... c=simple/simple; ...The first value is for headers. The second is for the body.
RFC 6376 defines two modes for each side:
simple: tolerate almost no changerelaxed: tolerate some whitespace cleanup and header line rewrappingThat wording matters. Whitespace cleanup is not the same as content rewrite.
The DKIM specification is explicit here: relaxed canonicalization can survive things like header field name lowercasing, unfolding wrapped headers, compressing whitespace runs, and ignoring trailing whitespace in body lines. It does not allow a downstream system to add new text to the body and still expect the original signature to validate.
Think of DKIM canonicalization as "normalization before hashing", not as a repair layer.
It helps when two systems represent the same content with slightly different whitespace. It does not help when one system changes the content itself.
That is why these two statements can both be true:
c=relaxed/relaxed is the safest common operational defaultc=relaxed/relaxed still breaks if an intermediate system adds a legal disclaimer footer or rewrites all linkssimple and relaxed really mean in practicesimple header canonicalizationFor headers, simple means the header must verify exactly as signed. Header field names are not case-folded, and whitespace is not normalized.
This is very strict. Even harmless-looking header formatting changes can break it.
relaxed header canonicalizationFor headers, relaxed allows a verifier to:
That makes it more resilient to normal transit handling.
But if a signed header value is materially changed, the signature still fails.
Example: if a downstream system rewrites Subject: or inserts text into a signed header value, relaxed canonicalization does not save it.
simple body canonicalizationFor the body, simple keeps content almost exactly as-is. The main allowance is around empty lines at the very end of the message body.
This means even small whitespace changes inside the body can invalidate the body hash.
relaxed body canonicalizationFor the body, relaxed ignores trailing whitespace at line ends, compresses internal whitespace runs, and ignores extra empty lines at the very end of the body.
This is why relaxed is common in production. It survives minor formatting adjustments.
It still does not survive:
This is the most common real-world example.
An outbound gateway, security appliance, or mailing platform appends something like:
If that footer is added after DKIM signing, the body hash no longer matches what the signer hashed.
Relaxed body canonicalization does not treat "extra lines added at the bottom" as ignorable, except for trailing empty lines. Actual footer text is new body content.
Google's own DKIM setup guidance warns admins to verify that outbound gateways do not interfere with DKIM, specifically because gateways may modify outgoing messages by adding a footer.
That warning is more important than it looks. Many teams technically "enabled DKIM" months ago, but a downstream mail hop is still mutating content after signing.
Link tracking often rewrites URLs from the original destination into a provider-owned redirect or tracking URL.
Example shape:
Original: https://example.com/reset
Rewritten: https://click.example.net/track/abc123If that rewrite happens before the final DKIM signing step, no problem. The signed body already contains the tracked URL.
If it happens after signing, the body changed and DKIM fails.
This is why mixed mail pipelines create confusing incidents. One sending path signs after all message transformations. Another signs early, then a later service rewrites links. Same domain, same selector, very different results.
MIME-related DKIM breakage is less obvious because the visible message may still look basically the same to a human.
Common triggers include:
A person sees "the same email". DKIM sees different canonicalized bytes.
That is enough for failure.
This also explains why some security relays or archive systems cause intermittent DKIM issues. They are not necessarily changing the visible business content. They are changing the wire-format representation after the message was signed.
Start with the full delivered headers, not sender-side assumptions.
Look for:
DKIM-Signature: including the c= valueAuthentication-Results: showing whether DKIM passed or failedIf header analysis still needs a refresher, DMARC troubleshooting: read Authentication-Results headers and fix alignment failures is the right companion.
Then apply this quick triage:
That may sound blunt, but it is the right bias operationally.
c= value can tell you how fragile the message isSome broad rules are useful:
simple/simple is fragile for most real-world mail flows.relaxed/relaxed is usually the most forgiving reasonable baseline.relaxed reduces whitespace-related failures. It does not excuse content mutation.So if a message already uses relaxed/relaxed and still fails, the next question is usually not "should canonicalization be more relaxed?" There is no more forgiving standard mode to switch to.
The next question is "what changed after signing?"
This shows up a lot when mail passes through a gateway that stamps "External message" warnings onto outbound or relayed mail.
If the body was signed upstream, that banner breaks DKIM.
The message body is no longer the body that was hashed.
This is one reason forwarded and list-expanded mail often loses the original DKIM result. DMARC, forwarding, mailing lists, SPF, DKIM, and ARC covers the broader forwarding side.
One region, one appliance cluster, or one connector behaves differently. That produces the classic "DKIM fails only for some recipients" incident.
For marketing mail, List-Unsubscribe and List-Unsubscribe-Post should be present before DKIM signing if they are meant to be signed and trusted. How to set up one-click unsubscribe for Gmail and Yahoo (RFC 8058) explains why those headers matter and why DKIM coverage of them matters too.
When canonicalization-related failure is confirmed, the clean fixes usually look like this:
That "sign last" rule prevents a lot of avoidable pain.
relaxed/relaxed means "DKIM can survive anything".DKIM canonicalization is not a feature that makes rewritten mail safe. It is a narrow normalization step that helps verifiers treat minor formatting differences as equivalent.
If a footer is appended, a tracked URL is substituted, or MIME structure is rewritten after signing, DKIM=fail is the expected result.
In practice, the most useful rule is this: transform first, sign last, then verify with real delivered headers.