DKIM canonicalization explained: why footers, link tracking, and MIME changes cause DKIM=fail

dkim

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.

What DKIM canonicalization actually does

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 change
  • relaxed: tolerate some whitespace cleanup and header line rewrapping

That 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.

The mental model that prevents confusion

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 default
  • c=relaxed/relaxed still breaks if an intermediate system adds a legal disclaimer footer or rewrites all links

What simple and relaxed really mean in practice

simple header canonicalization

For 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 canonicalization

For headers, relaxed allows a verifier to:

  • lowercase header field names
  • unfold wrapped header lines
  • compress multiple spaces or tabs into one space
  • ignore whitespace around the colon separator and at line ends

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 canonicalization

For 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 canonicalization

For 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:

  • adding a footer
  • removing text from the body
  • rewriting URLs to tracking domains
  • changing MIME part boundaries or encodings in ways that alter the canonicalized body

Why footers break DKIM so often

This is the most common real-world example.

An outbound gateway, security appliance, or mailing platform appends something like:

  • a legal disclaimer
  • a confidentiality notice
  • an external sender warning banner
  • a ticketing footer

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.

Why link tracking can break DKIM

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/abc123

If 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.

Why MIME changes are especially annoying to debug

MIME-related DKIM breakage is less obvious because the visible message may still look basically the same to a human.

Common triggers include:

  • converting quoted-printable to base64 or the reverse
  • changing multipart boundaries
  • rewrapping HTML lines differently
  • altering transfer encoding
  • inserting or modifying attachment-related MIME headers

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.

How to tell whether canonicalization is the issue

Start with the full delivered headers, not sender-side assumptions.

Look for:

  • DKIM-Signature: including the c= value
  • Authentication-Results: showing whether DKIM passed or failed
  • evidence of downstream systems that may have added banners, footers, tracking, or MIME rewriting

If 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:

  1. Check which system performed the final DKIM signing.
  2. List every message transformation after that point.
  3. Ask whether those transformations only normalize whitespace, or whether they rewrite content.
  4. Assume any body rewrite after signing is guilty until proven otherwise.

That may sound blunt, but it is the right bias operationally.

The c= value can tell you how fragile the message is

Some 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?"

Common breakage patterns

1. Security banner added after the sender signs

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.

2. ESP or gateway rewrites links downstream

The message body is no longer the body that was hashed.

3. A mailing list modifies the subject or footer

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.

4. MIME normalization changes happen on only one route

One region, one appliance cluster, or one connector behaves differently. That produces the classic "DKIM fails only for some recipients" incident.

5. One-click unsubscribe headers are added too late

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.

The fix is usually about message flow, not about DNS

When canonicalization-related failure is confirmed, the clean fixes usually look like this:

  1. Move DKIM signing to the last system that materially changes the message.
  2. Disable post-signing footers, banners, or link rewrites.
  3. Keep signing and message transformation inside one controlled platform when possible.
  4. Standardize the pipeline so all routes transform first and sign last.

That "sign last" rule prevents a lot of avoidable pain.

What not to do

  • Do not assume relaxed/relaxed means "DKIM can survive anything".
  • Do not troubleshoot only from DNS records.
  • Do not test one mail path and assume all other connectors behave the same way.
  • Do not let intermediate systems mutate content without deciding explicitly whether they are before or after the signing boundary.

Bottom line

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.

Previous PostNext Post