DKIM Signature Explained: Header Tags & Format

A deep dive into the DKIM-Signature header format. Learn what each tag means, how canonicalization works, and how receivers verify signatures.

Last updated: 2026-02-06

Every DKIM-authenticated email carries a DKIM-Signature header. This header contains everything a receiving server needs to verify that the message is authentic and unaltered. Understanding its structure is essential for debugging deliverability issues and configuring DKIM correctly.

Anatomy of a DKIM-Signature Header

Here is a complete, annotated example of a real DKIM-Signature header:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
  d=example.com; s=selector1;
  h=from:to:subject:date:message-id:content-type:mime-version;
  bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
  b=LjxC5FtTdY1k3E6gGZYqMwSMk5oN3DPBQf9IgrLwXnkYJm4vP8q
   R2T0xFKhd9vG3JlMqXwV+5e7UDAHyq2KjN0rMZDGvT5K8n...

Each tag in the header serves a specific purpose. Let's break them down.

Tag-by-Tag Breakdown

v= (Version)

v=1

The DKIM signature version. This is always 1. If you see any other value, the signature is malformed.

a= (Algorithm)

a=rsa-sha256

The signing algorithm. Two parts:

  • rsa — The cryptographic algorithm used to generate the signature
  • sha256 — The hash function used to digest the message

rsa-sha256 is the standard. The older rsa-sha1 is deprecated and should not be used — many receivers now reject it outright.

Avoid rsa-sha1. It is cryptographically weak and increasingly rejected by major mailbox providers including Gmail and Microsoft.

c= (Canonicalization)

c=relaxed/relaxed

Canonicalization defines how the message is normalized before hashing. The format is header/body, and each can be either simple or relaxed.

Simple canonicalization:

  • Headers: No changes allowed. The header must match byte-for-byte.
  • Body: Ignores trailing empty lines, but no other changes.

Relaxed canonicalization:

  • Headers: Converts header names to lowercase, reduces whitespace sequences to a single space, and removes trailing whitespace.
  • Body: Reduces whitespace sequences to a single space, removes trailing whitespace on each line, and ignores trailing empty lines.
ModeToleranceRisk of False FailuresUse Case
simple/simpleNoneHighHighly controlled environments
relaxed/relaxedHighLowMost email deployments
relaxed/simpleModerateMediumLenient headers, strict body
simple/relaxedModerateMediumStrict headers, lenient body

Use relaxed/relaxed

Email passes through many systems in transit — MTAs, security gateways, and spam filters. Relaxed canonicalization absorbs minor whitespace changes that would otherwise break your signature.

d= (Domain)

d=example.com

The signing domain. This is the domain claiming responsibility for the message. The receiving server uses this value (combined with the selector) to look up the public key in DNS.

The d= domain does not need to match the From: address domain exactly — but for DMARC alignment to pass, they must share the same organizational domain.

s= (Selector)

s=selector1

The selector identifies which DKIM key to use. The public key is published at selector._domainkey.domain.com in DNS. Selectors allow a domain to maintain multiple active keys — useful for key rotation or delegating signing to third-party services.

h= (Signed Headers)

h=from:to:subject:date:message-id:content-type:mime-version

This lists which email headers are included in the signature hash. The From header is always required. Other commonly signed headers include:

  • From (required) — The sender address
  • To — The recipient
  • Subject — The message subject
  • Date — The timestamp
  • Message-ID — Unique message identifier
  • Content-Type — MIME type of the body
  • MIME-Version — MIME version declaration

Only headers listed in the h= tag are protected by the signature. Headers not listed can be modified in transit without breaking DKIM verification. Always sign From, To, Subject, and Date at a minimum.

bh= (Body Hash)

bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=

A Base64-encoded hash of the message body, computed after canonicalization. The receiving server independently hashes the body and compares its result with this value. If they don't match, the signature fails — this is the "body hash did not verify" error.

The body hash is computed using the algorithm specified in a=. For rsa-sha256, the body is hashed with SHA-256.

b= (Signature)

b=LjxC5FtTdY1k3E6gGZYqMwSMk5oN3DPBQf9IgrLwXnkYJm4vP8q
   R2T0xFKhd9vG3JlMqXwV+5e7UDAHyq2KjN0rMZDGvT5K8n...

The actual digital signature. This is the RSA-encrypted hash of the canonicalized headers (including the bh= value). The receiving server decrypts it with the public key and compares it to its own hash of the signed headers.

This value is typically the longest tag in the header and is Base64-encoded.

Generate your DKIM keys

Create DKIM key pairs with properly formatted DNS records in seconds.

Generate DKIM Keys

Optional Tags

Beyond the core tags, a DKIM-Signature header may include these optional tags:

TagPurposeExample
`i=`Agent or user identity`i=user@example.com`
`t=`Signature timestamp (Unix epoch)`t=1706745600`
`x=`Signature expiration (Unix epoch)`x=1707350400`
`l=`Body length limit (number of bytes signed)`l=1024`
`q=`Query method for retrieving the public key`q=dns/txt`

The l= tag is dangerous. It specifies how many bytes of the body are signed. An attacker can append malicious content after the signed portion. Avoid using l= in production.

A Full Annotated Example

Here is a complete DKIM-Signature header with every tag explained:

DKIM-Signature: v=1;                          # Version: always 1
  a=rsa-sha256;                                # Algorithm: RSA with SHA-256
  c=relaxed/relaxed;                           # Canonicalization: relaxed for headers and body
  d=dkimcreator.com;                           # Signing domain
  s=2026q1;                                    # Selector (points to DNS record)
  t=1706745600;                                # Signed at: Unix timestamp
  x=1707350400;                                # Expires at: Unix timestamp (optional)
  h=from:to:subject:date:message-id;           # Signed headers
  bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIk     # Body hash (SHA-256, Base64)
     fJVOzv8=;
  b=LjxC5FtTdY1k3E6gGZYqMwSMk5oN3DPBQf9I     # Signature (RSA-encrypted hash, Base64)
     grLwXnkYJm4vP8qR2T0xFKhd9vG3JlMqXwV
     +5e7UDAHyq2KjN0rMZDGvT5K8n=

When a receiver gets this email, it:

1

Extract the d= and s= tags

Domain is dkimcreator.com, selector is 2026q1.

2

Query DNS for the public key

Looks up the TXT record at 2026q1._domainkey.dkimcreator.com.

3

Canonicalize the message

Applies relaxed/relaxed normalization to the headers and body.

4

Verify the body hash

Hashes the canonicalized body with SHA-256 and compares it to the bh= value.

5

Verify the header signature

Hashes the canonicalized signed headers, decrypts the b= value with the public key, and compares. If both match, DKIM passes.

Which Headers Should You Sign?

At minimum, sign these headers:

  • From — Required by the DKIM spec
  • To — Prevents recipient modification
  • Subject — Prevents subject tampering
  • Date — Prevents date manipulation
  • Message-ID — Prevents replay with altered identifiers

For additional protection, also sign:

  • Content-Type and MIME-Version — Prevents content type manipulation
  • Reply-To — Prevents redirect attacks
  • In-Reply-To and References — Prevents threading manipulation

Over-signing headers

You can list a header in h= that does not exist in the message. This prevents an attacker from adding that header later. For example, signing a non-existent Reply-To header prevents someone from injecting one in transit.

How Signature Verification Fails

DKIM signatures fail for several reasons:

Failure ReasonWhat Happened
Body hash mismatchMessage body was altered after signing
Signature mismatchSigned headers were modified in transit
Key not foundDNS record missing or selector wrong
Key revokedPublic key has empty `p=` tag
Algorithm unsupportedReceiver doesn't support the algorithm
Signature expiredCurrent time is past `x=` expiration

When debugging, look at the Authentication-Results header added by the receiving server. It will show exactly which check failed and why.


DKIM Creator generates properly signed keys so you can focus on deliverability, not debugging headers.

Generate DKIM keys in seconds

Create 1024 or 2048-bit RSA key pairs with correctly formatted DNS records. Free, instant, and browser-based.

Create DKIM Keys