DMARC Engine
Home/Blog/DKIM canonicalisation: simple vs relaxed
Blog

DKIM canonicalisation: simple vs relaxed

The DKIM c= tag decides whether your signature survives the journey to the inbox. Here is exactly what simple and relaxed canonicalisation do to headers and body, why relaxed tolerates the reformatting that real mail servers perform, and where simple still causes intermittent body-hash failures.

31 May 2026 · 15 min read

DKIM canonicalisation: simple vs relaxed

Every DKIM signature carries a small, easily overlooked tag that decides whether your mail survives the journey from your signing server to the recipient's inbox. That tag is c=, the canonicalisation algorithm. It tells the receiver exactly how to prepare the headers and body of the message before it recomputes the hashes and checks your signature. Get it wrong, or rather, pick the strict option without understanding the trade-off, and you sign up for a category of intermittent, maddening DKIM failures that no amount of key rotation will ever fix.

This article is about that one tag. Not what DKIM is in general (for that, start with What is DKIM), and not selectors or key length (those live in DKIM selectors explained and DKIM key length: 1024 vs 2048). This is specifically about canonicalisation: what it does, the precise byte-level difference between simple and relaxed for headers and for the body, why relaxed survives far more real-world mail handling, and the handful of cases where the choice genuinely bites you.

If you have ever seen dkim=fail (body hash did not verify) on a message that you are certain you signed correctly, canonicalisation is very often the reason. So let us look at exactly what the receiver is doing when it checks your signature.

What canonicalisation actually does

DKIM is a cryptographic signature over the content of an email. The signer computes a hash of the body, computes a hash of a chosen set of headers, and signs the result with a private key. The receiver fetches your public key from DNS and verifies that the hashes still match. If a single byte of the signed content changed in transit, the hashes do not match and the signature fails. That strictness is the whole point: DKIM exists to detect tampering and accidental modification.

Here is the problem. Email in transit is not a sealed envelope. Mail servers along the path routinely rewrite parts of a message without changing its meaning, and they have done so for decades:

  • They fold and unfold long header lines (a 998-character header gets wrapped across several physical lines, or unwrapped back).
  • They collapse runs of whitespace, convert tabs to spaces, or trim trailing spaces.
  • They add or remove blank lines at the very end of the body.
  • They change the capitalisation of header field names (From: versus FROM:).

None of these change what a human reads. All of them change the raw bytes. If DKIM compared raw bytes directly, almost no signature would survive a single hop, because the very first relay would reformat whitespace and break the hash.

Canonicalisation is the agreed-upon clean-up step that both sides run before hashing. The signer canonicalises, then hashes and signs. The receiver canonicalises the message it received in exactly the same way, then hashes and compares. By normalising away the changes that mail servers are expected to make, canonicalisation lets the signature tolerate harmless reformatting while still catching real modification.

RFC 6376 (the DKIM standard) defines two algorithms, and you choose one for the headers and one for the body. They are declared together in the signature's c= tag as header/body:

c=relaxed/relaxed
c=simple/simple
c=relaxed/simple
c=simple/relaxed

The first word is the header algorithm, the second is the body algorithm. The two named algorithms are simple and relaxed. The names are a little misleading: simple is the strict, unforgiving one, and relaxed is the tolerant one. Keep that straight and the rest follows.

Simple canonicalisation: barely any cleaning at all

simple does almost nothing. It is the conservative default in the spec, and it treats the message as very nearly byte-for-byte.

Simple header canonicalisation does not change the headers at all. The header field name, the colon, the value, the internal whitespace, the line folding, the case, all of it is hashed exactly as it appears. The only thing simple headers permit is nothing: the bytes must arrive identical to how they were signed.

Simple body canonicalisation does exactly one thing: it removes empty lines at the very end of the body, and ensures the body ends with a single CRLF. If the body is completely empty, it is treated as a single CRLF. That is the entire allowance. A trailing blank line dropped or added at the end of the message is tolerated. Everything else, every space, every tab, every fold, every change to a line in the middle of the body, must match precisely.

So with c=simple/simple, the only modification a relay can safely make is adding or stripping blank lines at the bottom of the message. Anything else breaks the signature.

To make this concrete, suppose your body is these three lines (showing line endings explicitly):

Hello there.<CRLF>
<CRLF>
Thanks,  Sam<CRLF>
<CRLF>
<CRLF>

Under simple, the two trailing empty lines collapse to nothing, leaving:

Hello there.<CRLF>
<CRLF>
Thanks,  Sam<CRLF>

Note that the two spaces between Thanks, and Sam are preserved exactly, and the blank line in the middle is preserved exactly. If any relay touches either of those, the body hash fails.

Relaxed canonicalisation: tolerant of harmless reformatting

relaxed does real normalisation. It anticipates the common, meaning-preserving changes that mail infrastructure makes and removes them before hashing, so both sides land on the same canonical form even if the wire bytes differed.

Relaxed header canonicalisation applies these rules to each signed header:

  1. Convert the header field name (the part before the colon) to lower case. Subject and subject and SUBJECT all become subject.
  2. Unfold the header so the whole value is on one logical line (remove the CRLF that line-folding inserted).
  3. Convert any sequence of whitespace (spaces and tabs) within the value to a single space.
  4. Remove all whitespace at the start and end of the value (trim leading and trailing spaces around the value, and remove the space before the colon if present).

Relaxed body canonicalisation applies these rules to the body:

  1. Reduce any sequence of whitespace within a line to a single space.
  2. Remove all trailing whitespace at the end of each line.
  3. Remove trailing empty lines at the end of the body (the same end-of-body handling as simple).

Take the same body. Under relaxed:

Hello there.<CRLF>
<CRLF>
Thanks, Sam<CRLF>

The double space between Thanks, and Sam has collapsed to a single space. Trailing spaces on any line would be gone. Trailing blank lines are gone. The result is a body that is identical even if a relay re-spaced the text or trimmed line endings, which is exactly what relays love to do.

The difference is not academic. Whitespace handling is the single most common reason a body hash silently changes in transit, and relaxed body canonicalisation is specifically designed to absorb it.

Why relaxed survives more in transit

The whole reason to care about this tag is survival. A signature that verifies on a perfect, untouched message is easy. A signature that still verifies after the message has bounced through a forwarder, a mailing list, a security gateway, and two or three relays is the one that keeps your mail aligned and your DMARC policy holding. Here is why relaxed survives where simple breaks.

Whitespace and line-wrapping are routinely rewritten. Plain-text bodies in particular get re-wrapped. A relay or client may rewrap lines to a different width, convert tabs to spaces, or strip trailing spaces that an editor left behind. simple body canonicalisation treats every one of those as tampering. relaxed collapses internal whitespace and strips trailing whitespace before hashing, so the same logical content produces the same hash regardless of how it was re-spaced.

Header folding is non-deterministic. Long headers like Subject, References, and DKIM-Signature itself get folded across multiple lines, and different mail systems fold at different points. A Subject line folded by your sender may be refolded by a relay. Under simple header canonicalisation, that refold changes the bytes and breaks the header hash. Under relaxed, the header is unfolded to one line and whitespace is collapsed first, so the fold position no longer matters.

Header case gets normalised. Some systems lower-case or otherwise rewrite header field names. relaxed lower-cases them before hashing, so From: versus from: is irrelevant. simple would treat that as a mismatch.

It composes well with the rest of email authentication. When you are trying to keep DKIM aligned across forwarders so that DMARC passes, every fragile assumption you can remove helps. relaxed/relaxed removes the whitespace and folding assumptions entirely, leaving only genuine body modification (a list footer, a "scanned by" banner, a subject tag) as a cause of failure. Those genuine modifications break any canonicalisation, and the answer to them is ARC and SPF, not a different c= value. See ARC explained and DKIM alignment and forwarding for what to do when the content itself is rewritten.

The short version: relaxed/relaxed does not make DKIM weaker in any way that matters for security. It still detects real changes to the meaningful content. It just stops treating cosmetic reformatting as an attack. That is why it is the de facto standard for signers that care about deliverability, and why nearly every major sending platform signs with it.

What the major senders actually use

You do not have to take this on faith. Look at the DKIM-Signature header of mail you receive from large providers and you will see the pattern. Google Workspace, Microsoft 365, Amazon SES, SendGrid, Mailchimp, Postmark and the rest overwhelmingly sign with c=relaxed/relaxed. A typical signature from a well-configured sender looks like this:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
  d=example.com; s=selector1; t=1718000000;
  h=from:to:subject:date:message-id;
  bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
  b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ
   VoG4ZHRNiYzR... (signature continues)

The c=relaxed/relaxed here is doing quiet, important work on every single message. You will occasionally see c=relaxed/simple from older or more conservative configurations, and the very occasional c=simple/simple from systems that have never revisited their defaults. If you control the signing configuration and you find simple anywhere in your c= tag, that is worth changing.

You can read a real signature from your own domain by sending a message to yourself and inspecting the source, or by running your domain and selector through the DKIM checker, which pulls the published public key and confirms it parses. The checker validates the key side of the equation; the c= tag is in the signature on the message itself, so to confirm what your signer uses you need to look at a real outbound message's headers.

Where simple still causes real failures

This is the practical payoff. If you are debugging a DKIM failure, canonicalisation belongs on your shortlist, and simple body canonicalisation is the usual culprit. Watch for these patterns.

Intermittent body-hash failures on paths that "should" be clean. If direct mail to a test mailbox passes but the same domain fails through a forwarder, a list, or an internal relay, and the failure reason is body hash did not verify, suspect that something in the path is re-spacing or re-wrapping the body. With simple body canonicalisation, a relay that strips trailing whitespace from each line is enough to break every signature. Switching the signer to relaxed/relaxed removes that entire class of failure. This exact scenario is one of the causes covered in Fix: DKIM signature did not verify.

Your own gateway modifies mail after signing. If a security appliance or outbound filter rewrites whitespace, re-wraps lines, or appends content after your application signs, you are breaking your own DKIM. The order must be: modify first, sign last. Even with relaxed, a footer added after signing changes the meaningful body and breaks the hash; but with simple, even a benign whitespace pass after signing breaks it. Signing must be the last thing that touches the message.

Conservative signers on legacy systems. Some on-premise mail systems and older MTAs default to simple/simple and have never been changed. If you administer such a system and you see DKIM holding up poorly in the wild, check the signing configuration. The fix is usually a one-line change to set relaxed/relaxed.

Re-encoding between transfer encodings. A relay that converts a body from quoted-printable to base64 (or rewraps base64 to different line lengths) changes the raw body bytes substantially. Neither simple nor relaxed fully protects against a full re-encode of the body, because canonicalisation operates on the decoded line structure, not the transfer encoding. But relaxed tolerates the line-wrapping differences that often accompany such handling, where simple does not. If a path re-encodes your mail, expect trouble regardless, and treat it as a forwarding problem (see Forwarding and DMARC).

One subtlety worth stating plainly: choosing relaxed does not fix content modification. If a mailing list adds [list-name] to the subject (a signed header) or appends an unsubscribe footer to the body, that is a genuine change to signed content, and it breaks the signature under any canonicalisation. The c= tag only governs how cosmetic reformatting is handled. For real content rewriting, the solution is alignment redundancy (DMARC passes if either DKIM or SPF aligns) and ARC, not a canonicalisation change. The interaction between DKIM, SPF and which mechanism aligns is laid out in SPF vs DKIM: which one aligns and DKIM alignment explained.

How to read the c= tag when diagnosing a failure

When you have a failing message in front of you, here is the order of operations.

  1. Find the failure reason in the Authentication-Results header. If it says body hash did not verify (or bh fail), the body changed in transit. If it says signature did not verify without "body hash", the header hash or the key is the issue. Canonicalisation is most relevant to the body-hash case.
  1. Read the c= tag in the DKIM-Signature header on the message. It tells you which algorithms the receiver used to check. If it reads c=simple/simple or c=relaxed/simple, the body was checked with simple, and a whitespace change anywhere in the body would have broken it.
  1. Compare a clean send with a routed send. Send the same content directly to a test mailbox at a major receiver and through the path that fails. If direct passes and routed fails on body hash, the path is modifying the body. If the signer uses simple body canonicalisation, switching to relaxed/relaxed may be all it takes to make the routed path pass, because the modification was cosmetic whitespace.
  1. Confirm the key side is healthy. Rule out a DNS or selector problem with the DKIM checker and Fix: DKIM key not found. If the key parses and resolves, your failure is in the message, and canonicalisation is back on the table.
  1. Use your DMARC reports to see which sources fail. Aggregate reports show which sending sources pass and fail DKIM, which tells you whether the problem is at signing or downstream. Drop a report into the DMARC report analyser and read it alongside Reading your first DMARC report.

The c= tag is small, but it is the difference between a signature that quietly survives the real internet and one that fails the first time a relay trims a trailing space.

How to set relaxed/relaxed on common signers

If you control your own signing, the change is almost always trivial. The exact knob depends on the system:

  • OpenDKIM (Postfix, Sendmail front-ends): set Canonicalization relaxed/relaxed in opendkim.conf.
  • Rspamd: the DKIM signing module signs with relaxed/relaxed by default in current versions; check dkim_signing.conf if you have overridden it.
  • Application-level signers (libraries in PHP, Python, Node, and so on): pass relaxed for both header and body canonicalisation when you construct the signer. The argument name varies but the value to choose is relaxed for each.
  • Hosted sending platforms (Google Workspace, Microsoft 365, SES, SendGrid, Mailchimp and friends): you do not set this; they already sign with relaxed/relaxed. There is nothing to change. Your job is only to publish their public key correctly, which is covered in the per-platform guides such as Amazon SES email authentication, SendGrid email authentication and Mailchimp email authentication.

After any change to signing, rotate or republish only if the key itself changed; a canonicalisation change does not require a new key. If you do need to roll keys, do it the safe way described in DKIM key rotation.

The practical takeaway

Canonicalisation is the clean-up step both the signer and the receiver run before they hash a message, so that harmless reformatting in transit does not destroy a valid signature. There are two algorithms per side. simple cleans almost nothing and breaks the moment a relay touches whitespace or refolds a header. relaxed normalises whitespace, header folding and header case, and as a result it survives the everyday reformatting that real mail infrastructure performs. For both headers and body, relaxed/relaxed is the right default, it is what the major senders use, and it removes a whole category of intermittent body-hash failures without weakening the protection that matters.

If you are signing your own mail, check the c= tag on a real outbound message and set relaxed/relaxed if it is anything else. If you are on a hosted platform, this is already handled and your effort belongs on publishing the key correctly and on the DKIM alignment that DMARC actually checks.

If you would rather not audit signers, canonicalisation tags and selectors by hand at all, that is exactly what DMARC Engine does for you: we take a domain from p=none to p=reject with no email outage, get DKIM signing and alignment right across every sending source, and email you when something downstream starts breaking signatures. Start by running your domain and selector through the DKIM checker, see what your signature actually says, and read What is DKIM if you want the full picture of how signing fits into DMARC.

Share

See where your domain stands today

Run a free DMARC scan, then let us take you to enforced p=reject with no email outage.