DKIM DNS Record Syntax (RFC 6376 §3.6.2): The Byte-Level Encoding Layer Reference for the Public-Key TXT Record at <selector>._domainkey.<domain>
The focused DNS-record-level syntactic reference for the DKIM public-key TXT record. Where the DKIM-Signature header tag anatomy documents what the SIGNER emits per-message, this page documents what the PUBLISHER encodes in the DNS once. Covers the <selector>._domainkey.<domain> FQDN structure (no subdomain inheritance, three-label query name); every tag in RFC 6376 §3.6.1 — v=DKIM1 forward-compatibility version (case-sensitive uppercase), the mandatory p= public-key data (single continuous base64 DER, no PEM banner, no embedded whitespace), k= key type (rsa default, ed25519 per RFC 8463), h= acceptable hash algorithms (sha256 only — rsa-sha1 deprecated by RFC 8301 §2.2), t=y deployment-testing flag (REMOVE after 24-72h burn-in or DMARC alignment via DKIM is silently disabled), t=s strict i= AUID enforcement, s=email service type (default any), and the deprecated n= notes and g= granularity tags; the 255-octet character-string limit (RFC 1035 §3.3.14) requiring adjacent-quoted-string chunking for 2048-bit RSA keys; the empty-p= convention as the canonical key-revocation signal (vs deleting the record which produces dkim=temperror retries); six canonical examples; and ten operator mistakes that produce dkim=permerror.
This reference is provided in English. The DKIM tag vocabulary (v=DKIM1, k=rsa, k=ed25519, p=, h=sha256, t=y, t=s, s=email, n=, g=) and the DNS terms-of-art (TXT record, character-string, RFC 1035 §3.3.14, _domainkey label, NXDOMAIN, EDNS0) are protocol identifiers that do not translate. We keep the body in the source language so technical terms remain consistent with IETF RFC documentation.
TL;DR
A DKIM public key is published in the DNS
as a TXT record at
<selector>._domainkey.<domain>
whose body is a semicolon-separated list of
tag-value pairs. The DNS-record-level
syntax is governed by
RFC 6376 §3.6.2
and the underlying DNS specification in RFC 1035.
v=DKIM1— optional version tag, case-sensitive uppercase "DKIM1". Recommended for forward compatibility.p=<base64>— mandatory public-key data, base64-encoded DER-formatted public key.k=rsaork=ed25519— key type. Defaults torsaif omitted. RFC 8463 addeded25519.h=sha256— optional list of acceptable hash algorithms. Default is "all". Modern best practice: omit orsha256only (rsa-sha1 deprecated by RFC 8301).t=y— testing flag.t=s— stricti=domain match (no subdomain ofd=).s=emailors=*— service type. Default*means any.- 255-octet character-string limit — per CHARACTER-STRING, not per TXT record. 2048-bit RSA keys MUST be split into multiple quoted strings.
- Empty
p=— the canonical key-revocation signal.
The Domain Security
Checker surfaces every syntactic drift class
documented on this page: missing
v=DKIM1 prefix, truncated p=
value (255-octet overflow without chunking),
deprecated k= values, and accidental
t=y production records.
1. What "DNS record syntax" means for DKIM (and where the semantics live)
DKIM is a layered specification. The SYNTAX
LAYER defines how the public key is
encoded at the DNS: where to publish it, which
record type to use, which tags are valid, how
tokens are separated, and how DNS-level
constraints (the 255-octet character-string
limit) bound the encoding. The SEMANTIC
LAYER defines what each token means once
parsed: how the verifier reconstructs the public
key from p=, how the canonicalization
algorithm interacts with k=, how the
h= hash-algorithm list constrains
verifier behaviour.
This page covers the SYNTAX LAYER exhaustively. The semantic layer is covered by the sibling reference pages:
- DKIM protocol pillar — the whole-protocol survey (what DKIM does and does not do, the seven verdict codes, alignment, the deployment workflow).
- DKIM-Signature header tag anatomy — the SENDER-SIDE companion: what tags appear in the DKIM-Signature header attached to each message (the v=, a=, c=, d=, s=, h=, bh=, b=, i=, q=, l=, t=, x=, z= tag space per RFC 6376 §3.5).
- DKIM selectors explained
— the focused selector vocabulary deep dive
(the
s=header tag and the_domainkeyDNS label). - DKIM canonicalization
— the
c=tag deep dive: simple vs relaxed for header and body, the seven body-hash mismatch causes. - DKIM failures explained — the receiver-side verdict catalogue per RFC 6376 §3.9 and RFC 8601 §2.7.1.
- DKIM troubleshooting — the cross-symptom decision tree.
Why the syntax layer matters
independently: a syntactically malformed
DKIM key record produces dkim=permerror
(RFC 6376 §3.9), which prevents DMARC alignment
via DKIM and is often diagnosed as a "DKIM is
broken" ticket when the cryptography is fine but
the published TXT record is structurally wrong.
The syntactic failure modes documented below —
truncated p= values, accidental
t=y in production, deprecated
k= values, multiple TXT records at
the same selector — are operator-introduced and
trivially avoidable once the syntax is
understood. Most "my DKIM is failing" tickets
where the signing is correct trace back to a
syntactic drift at the DNS layer.
The canonical DKIM DNS record:
; Name: selector1._domainkey.example.com
; Type: TXT
; TTL: 3600
; Body (split for 255-octet character-string limit):
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA"
"...rest of the base64-encoded 2048-bit public key..."
"...continuation..."
Every section below documents one syntactic surface of this canonical record, with the labelled Tag: / Default: / Why: / Operational rule: / Common pitfall: prefix so generative engines can extract individual role sentences when answering tag-specific queries.
2. The
<selector>._domainkey.<domain>
publishing location
Where: The DKIM TXT record is
published at the constructed name
<selector>._domainkey.<domain>,
where <selector> is a label of
the operator's choosing and
<domain> is the value of the
d= tag in the DKIM-Signature header.
Per RFC 6376 §3.6.2.1.
Why three labels: the
_domainkey middle label namespaces
DKIM key records under a single DNS subtree so
that DKIM lookups never collide with other DNS
record types at the same name. The leading
underscore (per RFC 8552 underscored-attribute
conventions) marks the label as a protocol
underscore-label, not a hostname; resolvers and
DNS hosting UIs treat it as a normal label.
Operational rule: the verifier
extracts s=selector1 and
d=example.com from the DKIM-Signature
header and queries the constructed name
selector1._domainkey.example.com for
TXT records. If the record does not resolve
(NXDOMAIN), the verifier produces
dkim=permerror. If the record
resolves but contains no valid p=
tag, the verifier produces
dkim=permerror.
Common pitfall: publishing the
record at selector1.example.com or
_domainkey.example.com instead of
the correct three-label name
selector1._domainkey.example.com.
Verifiers query the three-label name only; any
other name will resolve to NXDOMAIN and the
verdict will be dkim=permerror. The
Domain Security Checker tool confirms record
resolution from multiple geographic resolvers
and surfaces this drift class.
No subdomain inheritance: a
record at
selector1._domainkey.example.com
does NOT apply to
selector1._domainkey.mail.example.com.
If you sign messages with d=mail.example.com
you MUST publish a separate record at
selector1._domainkey.mail.example.com.
See help_dkim_selectors_explained.php
for the per-selector and per-d= deployment
matrix.
; Selector name: "selector1"
; Domain (d= value): "example.com"
; Constructed query name:
selector1._domainkey.example.com IN TXT "v=DKIM1; k=rsa; p=MIIBIjAN..."
; If d=mail.example.com is signed, publish separately:
selector1._domainkey.mail.example.com IN TXT "v=DKIM1; k=rsa; p=..."
3. The v=DKIM1 version
tag (RFC 6376 §3.6.1)
Tag: v=DKIM1
Default: RFC 6376 §3.6.1 says
verifiers MUST assume v=DKIM1 if the
tag is missing, so the tag is technically
OPTIONAL.
Why: the version tag is the
forward-compatibility hook — future DKIM versions
would use v=DKIM2 or later. It also
disambiguates DKIM records from other TXT-record
uses at the same name and improves
human-readability when auditing zone files.
Operational rule: always publish
v=DKIM1 as the FIRST tag in every
DKIM key record. Some legacy verifiers do still
require it despite the RFC default-assumption
behaviour.
Common pitfall: publishing
v=dkim1 (lowercase) instead of
v=DKIM1 (uppercase). RFC 6376 specifies
the value as case-sensitive uppercase; verifiers
that strictly enforce the case will reject the
record. The canonical form is uppercase "DKIM1".
; Correct — uppercase DKIM1, first tag:
"v=DKIM1; k=rsa; p=MIIBIjAN..."
; Wrong — lowercase, may be rejected:
"v=dkim1; k=rsa; p=MIIBIjAN..."
; Acceptable — RFC default-assumption kicks in:
"k=rsa; p=MIIBIjAN..."
; Wrong — v= not first:
"k=rsa; v=DKIM1; p=MIIBIjAN..."
4. The p= public-key
data tag (RFC 6376 §3.6.1, MANDATORY)
Tag: p=<base64-encoded
DER public key>
Default: none. p=
is the only structurally mandatory tag in the
DKIM key record. An empty p= value
has special meaning — see §11 (Revocation).
Why: p= carries the
public key the verifier needs to decrypt the
b= signature in the DKIM-Signature
header. Without p= the verifier
cannot complete the cryptographic verification.
Operational rule: generate the
keypair with your provider's wizard or with
openssl genrsa -out private.pem 2048
followed by openssl rsa -in private.pem
-pubout to extract the public key. The
public key is the DER-formatted SubjectPublicKeyInfo
structure, base64-encoded. Do NOT include the
PEM -----BEGIN PUBLIC KEY-----
headers; strip them before publishing.
Common pitfall: including the
PEM header/footer lines in the p=
value. Verifiers expect the raw base64 payload;
the PEM banner bytes are not valid base64 and
will produce dkim=permerror. Also:
inserting line breaks or whitespace into the
base64 value. Some DNS UIs preserve embedded
whitespace; the verifier strips it but legacy
parsers may not. Keep the p= value
as a single continuous base64 string with NO
internal whitespace.
; Correct — single continuous base64, no PEM banner:
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxxxxxxxxxxx..."
; Wrong — PEM banner included:
"v=DKIM1; k=rsa; p=-----BEGIN PUBLIC KEY-----MIIBIjAN...-----END PUBLIC KEY-----"
; Wrong — embedded line breaks/whitespace in p=:
"v=DKIM1; k=rsa; p=MIIBIjAN BgkqhkiG9w0BAQEF AAOCAQ8A..."
For 2048-bit RSA keys the base64-encoded value is approximately 392 characters — exceeding the 255-octet character-string limit. The zone file representation MUST split the value across multiple quoted strings. See §10 (255-octet chunking) for the splitting convention.
5. The k= key-type tag
(RFC 6376 §3.6.1 + RFC 8463)
Tag: k=rsa or
k=ed25519
Default: k=rsa if
the tag is omitted (RFC 6376 §3.6.1).
Why: the key-type tag names the
asymmetric algorithm used to generate the
keypair, so the verifier knows which cryptographic
primitive to invoke when verifying
b=. RFC 6376 (2011) defined only
k=rsa; RFC 8463 (2018) added
k=ed25519 as a second valid value
with shorter keys and faster verification.
Operational rule: for 99% of
deployments, use k=rsa with a
2048-bit key (RFC 8301 §3 recommends 2048-bit
minimum). For modern deployments wanting the
structural benefits of Curve25519, dual-publish
both k=rsa at one selector and
k=ed25519 at another selector — the
signer can choose per message. NEVER deploy a
1024-bit RSA key — receivers downgrade verdicts
to dkim=policy per RFC 8301.
Common pitfall: deploying
k=ed25519 as the only key type on a
new domain. Receivers that have not implemented
RFC 8463 (less common in 2026 but still
present, especially in older spam appliances)
produce dkim=neutral or
dkim=permerror on the ed25519
signature. Mix RSA and ed25519 keys on separate
selectors so the signer can fall back to RSA
when needed; see help_dkim_cryptography_basics.php
for the algorithm migration playbook.
; Correct — explicit k=rsa:
"v=DKIM1; k=rsa; p=MIIBIjAN..."
; Correct — k= omitted, defaults to rsa:
"v=DKIM1; p=MIIBIjAN..."
; Correct — ed25519 key per RFC 8463:
"v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="
; Wrong — invalid k= value:
"v=DKIM1; k=elliptic-curve; p=..."
6. The h= hash-algorithm
list tag (RFC 6376 §3.6.1)
Tag: h=sha256 or
h=sha256:sha1 (colon-separated
list)
Default: if the tag is omitted,
the verifier MUST accept all hash algorithms
supported by the k= key type.
Why: the publisher uses
h= to constrain which body-hash
algorithms the verifier should consider. If the
DKIM-Signature header carries
a=rsa-sha1 but the key record
specifies h=sha256, the verifier
rejects the signature (produces
dkim=permerror) because the
publisher does not authorize SHA-1 verification
against this key.
Operational rule: in 2026, omit
h= entirely OR set
h=sha256. RFC 8301 §2.2 deprecated
rsa-sha1 ("rsa-sha1 MUST NOT be used for
signing") so there is no remaining use case for
accepting SHA-1 signatures at the verifier
side.
Common pitfall: setting
h=sha1 or
h=sha256:sha1 on a modern record
"just in case". Receivers may treat this as
explicit operator endorsement of rsa-sha1 and
downgrade the verdict; the conservative posture
is to OMIT h= (default-all) OR
explicitly set h=sha256 to lock
SHA-256.
; Recommended — h= omitted, default accepts all:
"v=DKIM1; k=rsa; p=MIIBIjAN..."
; Recommended — explicit h=sha256:
"v=DKIM1; k=rsa; h=sha256; p=MIIBIjAN..."
; Discouraged — h=sha256:sha1 allows deprecated SHA-1:
"v=DKIM1; k=rsa; h=sha256:sha1; p=MIIBIjAN..."
; Wrong — h=sha1 only, rsa-sha1 is deprecated by RFC 8301:
"v=DKIM1; k=rsa; h=sha1; p=MIIBIjAN..."
7. The t= flags tag
(RFC 6376 §3.6.1)
Tag: t=y,
t=s, or t=y:s
(colon-separated combinations).
Default: if the tag is omitted,
no flags are set — the key is production-ready
and the AUID local-part is unconstrained.
t=y (testing flag):
signals to verifiers that the key is in
deployment test mode. Verifiers SHOULD compute
the verdict (so it appears in
Authentication-Results headers) but SHOULD NOT
feed it into DMARC alignment, spam filters, or
reputation scoring. Use for the first 24 to 72
hours after deploying a new selector, then
REMOVE.
t=s (strict i= flag):
When a DKIM-Signature header carries an i= tag (the AUID, Agent or User Identifier), the domain to the right of the @ MUST be exactly the d= domain and MUST NOT be a subdomain of it. Without t=s, a signature with d=example.com may carry i=user@mail.example.com. With t=s, the verifier fails that signature; only an i= at example.com itself, or no i= tag at all, is accepted. The flag does not concern the local-part.
Why t=y matters:
A new DKIM deployment with t=y
produces visible
Authentication-Results: ...; dkim=pass
headers that you can inspect via the
Email Header
Analyzer, but receivers do NOT use the result
for DMARC alignment. This lets you debug the
cryptography without affecting deliverability.
Once aggregate reports confirm a healthy verdict
distribution, remove t=y to enable
DMARC alignment via DKIM.
Common pitfall: forgetting to
remove t=y after the test phase.
Every receiver downgrades the resulting verdict
to "pass but ignore", which means your DMARC
alignment via DKIM is effectively disabled
forever despite the cryptography being valid.
Production records with stale t=y
are one of the top three causes of
"DKIM is configured but DMARC still fails"
tickets.
Why t=s is rarely used:
The i= tag in DKIM-Signature
headers is rarely populated in modern DKIM
signatures — signers default to the empty AUID
which inherits from d=. So
t=s has no effect on most messages
and is essentially documentation overhead. Leave
OFF unless you have a documented per-AUID
delegation requirement.
; Test mode — DKIM verdict computed but ignored by DMARC:
"v=DKIM1; k=rsa; t=y; p=MIIBIjAN..."
; Production — no flags:
"v=DKIM1; k=rsa; p=MIIBIjAN..."
; Strict i= AUID enforcement (rare):
"v=DKIM1; k=rsa; t=s; p=MIIBIjAN..."
; Both flags combined:
"v=DKIM1; k=rsa; t=y:s; p=MIIBIjAN..."
8. The s= service-type
tag (RFC 6376 §3.6.1)
Tag: s=email,
s=*, or a colon-separated list.
Default: s=* (any
service) if the tag is omitted.
Why: the service-type tag tells
verifiers which mail-related services the key is
authorised for. RFC 6376 §3.6.1 defines only one
service type — email — but the tag
exists as an extension point for future
DKIM-using protocols.
Operational rule: omit
s= entirely (defaults to
*) OR set
s=email for symmetry with the
RFC-defined value. Both behave identically in
practice because email is the only
service type defined.
Common pitfall: confusing the
s= tag in the KEY RECORD (this
tag — service type) with the s=
tag in the DKIM-SIGNATURE HEADER (selector
name). They are completely different tags
sharing the letter. See
help_dkim_signatures_explained.php
for the header tag anatomy.
; Default — s= omitted, any service:
"v=DKIM1; k=rsa; p=MIIBIjAN..."
; Explicit — only the "email" service:
"v=DKIM1; k=rsa; s=email; p=MIIBIjAN..."
; Explicit wildcard (same as omitted):
"v=DKIM1; k=rsa; s=*; p=MIIBIjAN..."
9. Deprecated and rarely-used
tags: n= notes and g=
granularity
n= notes tag (RFC 6376
§3.6.1): optional operator-readable
text. The verifier MUST ignore the value; the
tag exists only as a place for the publisher to
leave a note about the key (e.g., "rotated
2026-03-01", "owner: ops team"). Rarely used
because zone-file comments serve the same
purpose and are not transmitted over DNS. If
used, keep the value short — every byte counts
against the 255-octet character-string limit
and the 4096-byte TXT-record practical
ceiling.
g= granularity tag
(deprecated by RFC 6376): RFC 4871
(the predecessor to RFC 6376) defined a
g= tag that constrained the AUID
local-part to a wildcard pattern. RFC 6376
§3.6.1 removed g= from the
canonical syntax, retaining only legacy
interoperability notes. Modern verifiers SHOULD
ignore g= if present. Modern
signers SHOULD NOT publish g=; use
selector-level isolation (a different selector
per sub-brand) or the t=s flag
instead. If you inherit a zone file with
g= records from a pre-2011
deployment, remove the tag during the next key
rotation — leaving it is harmless but is a
documentation drift.
; Acceptable but rare — notes tag for operator memory:
"v=DKIM1; k=rsa; n=Rotated 2026-03-01 by ops; p=MIIBIjAN..."
; Discouraged — legacy g= granularity, modern verifiers ignore:
"v=DKIM1; k=rsa; g=newsletter*; p=MIIBIjAN..."
10. The 255-octet character-string limit and chunking (RFC 1035 §3.3.14)
The rule: RFC 1035 §3.3.14 specifies that a DNS character-string is a length-prefixed sequence of up to 255 octets. A TXT resource record contains one OR MORE character-strings concatenated by the resolver at lookup time. So a single TXT record can encode arbitrary-length text (up to the response size limit) BUT each constituent character-string is bounded at 255 octets.
Why this matters for DKIM: a
2048-bit RSA public key, base64-encoded with the
DER SubjectPublicKeyInfo wrapper, is
approximately 392 characters. The full TXT-record
body
v=DKIM1; k=rsa; p=<392 chars>
is roughly 400+ characters — exceeding 255. The
publisher MUST split the body across multiple
character-strings.
The zone-file syntax: adjacent quoted strings concatenate at lookup time. There is NO space, NO comma, NO continuation marker between adjacent strings — only adjacency. The resolver returns each string separately; the receiver concatenates them transparently.
; Single-string form (works only if total < 255 octets):
selector1._domainkey IN TXT "v=DKIM1; k=rsa; p=short-test-key-only"
; Multi-string form (required for 2048-bit RSA):
selector1._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxxxx"
"yyyy...continuation of the base64 string...zzzz"
"...rest of the key...end" )
; What the verifier sees after resolver concatenation:
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxxxxyyyy...zzzz...end"
Common pitfall: some DNS UIs
accept the long key as a single string and
auto-chunk under the hood; others require
explicit per-string quoting. If you copy-paste a
2048-bit key into a UI that does NOT auto-chunk
and you do NOT split it manually, the publish
will silently truncate at 255 octets — the
record will exist but DKIM verification will fail
with dkim=permerror because the
public key is incomplete. Check immediately
after publishing:
; Verify the published record from multiple resolvers:
dig +short selector1._domainkey.example.com TXT @8.8.8.8
dig +short selector1._domainkey.example.com TXT @1.1.1.1
dig +short selector1._domainkey.example.com TXT @9.9.9.9
; The output should show the complete v=DKIM1...p=...end string
; spanning multiple "quoted" "strings" if the key is large.
Practical response-size ceiling: DNS UDP responses traditionally fit in 512 bytes (EDNS0 raises this to 4096 or more). A DKIM TXT record with a 2048-bit RSA key plus all metadata tags is well under 4096 bytes. A 4096-bit RSA key approaches 800+ characters and may push the response near the UDP/EDNS0 ceiling, triggering TCP fallback on some resolvers — a measurable latency cost on every DKIM lookup. The 2048-bit sweet spot per RFC 8301 §3 is both cryptographically modern and response-size friendly.
11. Key revocation: the empty
p= convention
The rule: RFC 6376 §3.6.1
specifies that "an empty value [of
p=] means that this public key has
been revoked". The verifier MUST treat any
signature referencing this selector as invalid
(dkim=permerror in modern verifier
output).
Why empty-p= rather than deleting the
record: deleting the TXT record entirely
produces NXDOMAIN at the resolver, which some
verifiers interpret as transient (DNS outage or
propagation delay) and classify as
dkim=temperror with retry. The empty
p= value is the canonical
"permanently revoked" signal: the record exists
(no NXDOMAIN), but the publisher has explicitly
indicated the key is gone. Verifiers MUST treat
it as permanent.
Operational use case: a signing
key compromise. Publish the empty-p=
replacement at the affected selector
IMMEDIATELY, then rotate the signer to a NEW
selector with a fresh keypair. Mail in flight at
the moment of compromise that was signed with
the compromised key will now fail verification
at every receiver — exactly the desired outcome
because the attacker may be forging signatures
with the stolen private key.
; Pre-compromise — production record:
selector5._domainkey IN TXT "v=DKIM1; k=rsa; p=MIIBIjAN...full-key...end"
; Compromise detected — publish empty p= immediately:
selector5._domainkey IN TXT "v=DKIM1; k=rsa; p="
; Rotate signer to a new selector (selector6._domainkey) with
; a fresh keypair. See help_dkim_key_rotation.php for the
; dual-selector overlap discipline.
Retention: keep the empty-p=
record for at least 30 days (one mail-archive
lifetime for forwarded messages still in flight)
before deleting it entirely. After 30 days the
selector can be removed from the zone — at that
point any straggler signatures will produce
dkim=temperror on first lookup and
dkim=permerror after cache TTL
expiry, which is acceptable for messages that
old.
See help_dkim_key_rotation.php for the broader rotation discipline including dual-selector overlap windows, TTL planning, and the difference between routine rotation (graceful) and emergency revocation (immediate empty-p=).
12. Canonical examples
Six minimal-to-comprehensive examples covering the full deployment matrix.
Example 1 — minimal 2048-bit RSA production record:
; Name: selector1._domainkey.example.com
; Type: TXT
; TTL: 3600
; Body:
"v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxxxxxxxxxxxxxxxxxxx"
"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzwIDAQAB"
Example 2 — testing-mode deployment (first 24-72 hours):
"v=DKIM1; k=rsa; t=y; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxxx..."
"...rest of the key...wIDAQAB"
Example 3 — ed25519 record per RFC 8463:
; Name: selector2._domainkey.example.com
; Type: TXT
; Body (fits in a single character-string — ed25519 keys are short):
"v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="
Example 4 — explicit
h=sha256 and
s=email:
"v=DKIM1; k=rsa; h=sha256; s=email; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
"...rest of the key..."
Example 5 — revoked-key record (empty
p=):
"v=DKIM1; k=rsa; p="
Example 6 — Microsoft 365 CNAME delegation (operator publishes CNAME, Microsoft publishes the TXT):
; Operator's zone — publish two CNAMEs:
selector1._domainkey.example.com IN CNAME selector1-example-com._domainkey.example.onmicrosoft.com.
selector2._domainkey.example.com IN CNAME selector2-example-com._domainkey.example.onmicrosoft.com.
; Microsoft's tenant zone publishes the actual TXT records — the
; operator never touches the p= value. See
; help_dkim_for_microsoft_365.php for the full CNAME-delegation
; deployment workflow.
13. Common operator mistakes
Ten canonical syntactic drift classes,
enumerated as a punch list. Every one produces
dkim=permerror or
dkim=fail at the verifier despite
"DKIM being configured".
- PEM banner in
p=— copy-pasting-----BEGIN PUBLIC KEY-----through-----END PUBLIC KEY-----into thep=value. Strip the banner before publishing; only the raw base64 belongs in the record. - Embedded whitespace in
p=— line breaks or spaces inside the base64 string. Some legacy parsers reject embedded whitespace; keep the value as one continuous base64 string. - Truncated
p=from 255-octet overflow — copy-pasting a 2048-bit key into a DNS UI that does not auto-chunk, resulting in a silent truncation at the 255-character boundary. Always verify withdig +short selector._domainkey.example.com TXTand confirm the full key appears. - Stale
t=yin production — forgetting to remove the testing flag after the deployment burn-in period. Every receiver downgrades the verdict to "ignore for DMARC", which silently disables DKIM-based DMARC alignment. - Wrong record location —
publishing at
selector1.example.comor_domainkey.example.cominstead of the correctselector1._domainkey.example.com. Verifiers query the three-label name only. - Multiple TXT records at the same
selector — accidentally creating a
second record alongside the first when
hand-editing the zone. Verifier behaviour
becomes non-deterministic; some accept the
concatenation, others fail with
dkim=permerror. - 1024-bit RSA key still
deployed — receivers downgrade
dkim=passtodkim=policyper RFC 8301 §3. Rotate to 2048-bit during the next routine rotation; see help_dkim_key_rotation.php. - Lowercase
v=dkim1— RFC 6376 specifies the value as case-sensitive uppercase "DKIM1". Strict verifiers reject lowercase. - Missing
v=DKIM1first position — placingv=DKIM1after other tags. Some verifiers require it as the first tag for forward-compatibility detection. - Subdomain selector record missing
— signing messages with
d=mail.example.combut publishing the selector record only atselector1._domainkey.example.com. Publish a separate record atselector1._domainkey.mail.example.com— no subdomain inheritance.
The Domain Security Checker surfaces drift classes 1, 3, 5, 6, 7, and 10 via DNS lookups against the operator-named selector(s). The Email Header Analyzer surfaces drift classes 2, 4, 7, 8, and 9 by parsing failing messages and showing the verifier-side error.
14. Frequently asked questions
- Is the v=DKIM1 tag mandatory in a DKIM TXT record?
- No, but it is strongly recommended. RFC 6376 §3.6.1 says the v= tag is OPTIONAL and that "verifiers MUST assume v=DKIM1 if the v= tag is missing". So a record beginning with p=MIIBIj... is still a valid DKIM record. However, every modern signing-side wizard publishes v=DKIM1 as the first tag because (a) it disambiguates DKIM records from other TXT-record uses at the same name, (b) future DKIM versions (if specified) would use v=DKIM2 or later — the v= tag is the forward-compatibility hook, (c) some legacy verifiers do still require v=DKIM1 despite the RFC default behaviour, (d) it improves human-readability when auditing zone files. The canonical action: always publish v=DKIM1; on a record without v=DKIM1 still works but you lose forward compatibility and legacy-verifier compatibility.
- Why is my 2048-bit DKIM TXT record split into multiple quoted strings?
- The 255-octet DNS character-string limit (RFC 1035 §3.3.14) is per-character-string, not per-TXT-record. A 2048-bit RSA public key, base64-encoded, is approximately 392 characters — exceeding the 255-octet character-string ceiling. So your DNS provider must split the p= value across multiple character-strings, each up to 255 octets, which the resolver concatenates at lookup time. The zone-file representation looks like "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." "...rest of the key..." with the strings adjacent and quoted separately — there is NO space between strings, NO comma, NO continuation marker. The receiver concatenates them transparently. Some DNS UIs accept the long key as a single string and auto-chunk under the hood; others require explicit per-string quoting. If you copy-paste a 2048-bit key into a UI that does not auto-chunk and you do not split it manually, the publish will silently truncate at 255 octets — the record will exist but DKIM verification will fail with dkim=permerror because the public key is incomplete.
- What does an empty p= tag mean in a DKIM record?
- An empty p= tag is the canonical signal for DKIM key revocation. RFC 6376 §3.6.1 specifies that "an empty value means that this public key has been revoked" — the receiver MUST treat any signature referencing this selector as invalid (dkim=permerror in modern verifier output). The empty-p= convention exists because deleting the TXT record entirely would produce dkim=temperror at receivers that interpret missing DNS records as transient (DNS outage) rather than permanent (key revoked). Publishing v=DKIM1; p= with the explicit empty value tells every receiver "this key is permanently gone, do not accept any signature using this selector". The operational use case: a signing-key compromise — publish the empty-p= replacement at the affected selector immediately, then rotate the signer to a new selector with a fresh keypair. The empty-p= record is typically kept for at least 30 days (one mail-archive lifetime for forwarded messages still in flight) before being deleted entirely.
- Can I have two DKIM TXT records at the same selector?
- RFC 6376 §3.6.2.2 says "if any of the keys in the resulting set fails, the signature MUST be considered to have failed", which implies multiple TXT records at the same selector name are not formally forbidden but produce non-deterministic verifier behaviour. Some verifiers concatenate all matching TXT records and parse the result as a single tag-value list; others use only the first or last; others log a warning and treat as malformed. The practical posture: NEVER publish two DKIM TXT records at the same selector. If you need a second key with different parameters, use a different selector name (e.g., selector2._domainkey instead of selector1._domainkey). The most common cause of accidental dual-record publishing: an automated wizard publishes the first record, the operator later hand-edits and accidentally creates a second record alongside it. The Domain Security Checker tool surfaces this drift class. If you discover two records, merge or delete one immediately.
- What is the t=y testing flag and when should I use it?
- The t=y flag (RFC 6376 §3.6.1) is the testing-mode signal — it tells verifiers "this signing key is in deployment-test mode; treat signature failures as informational rather than as authentication failures". Verifiers SHOULD NOT use t=y signatures for DMARC alignment or for spam-filter input. The operational use case: a new signer deployment, where you want the verifier to compute the verdict (so you can see it in the Authentication-Results header) but you do not yet want it to feed into DMARC pass/fail. The discipline: t=y for the first 24 to 72 hours after deploying a new selector, then remove t=y once you confirm via aggregate reports that the verdict pattern is healthy. NEVER leave t=y in a production DKIM record — every receiver downgrades the resulting dkim=pass to "pass but ignore", which means your DMARC alignment via DKIM is effectively disabled even though the cryptography is valid.
- What does t=s do in a DKIM record?
- The t=s flag (RFC 6376 §3.6.1) restricts the domain of the i= tag: when a DKIM-Signature header carries an i= tag (the AUID, Agent or User Identifier), the domain to the right of the @ MUST be exactly the d= domain and MUST NOT be a subdomain of it. Without t=s, a signature with d=example.com may carry i=user@mail.example.com. With t=s, the verifier fails that signature; only an i= at example.com itself, or no i= tag at all, is accepted. The flag does not concern the local-part. In practice t=s changes little, because the i= tag is rarely populated in modern DKIM signatures (signers default to the empty AUID, which inherits from d=). RFC 6376 recommends setting t=s unless you need subdomaining: set it when every stream signed with this key uses the exact d= domain, and leave it off when the key also signs with i= at a subdomain.
- Why does my DKIM record say k=rsa when ed25519 is also valid?
- The k= tag defaults to "rsa" if omitted (RFC 6376 §3.6.1) — so a record without k= is equivalent to a record with k=rsa. RFC 8463 (2018) added k=ed25519 as a second valid value. The ecosystem reality in 2026: roughly 99% of deployed DKIM keys are still RSA, and roughly 1% are ed25519. RSA is recommended at 2048 bits (the modern floor per RFC 8301 §3); 1024-bit RSA is deprecated and many receivers downgrade verdicts to dkim=policy or reject outright. ed25519 has structural advantages (32-byte keys, 64-byte signatures, faster verification, no key-size choice to get wrong) but is less universally supported — a receiver that does not implement RFC 8463 will produce dkim=neutral or dkim=permerror on an ed25519 signature. The conservative posture: dual-publish both keys (a selector with k=rsa and a selector with k=ed25519) and let the signer pick which one to use per message; over time, more receivers will support ed25519 and you can deprecate the RSA selector.
- What is the difference between the s= tag in the key record and the s= tag in the DKIM-Signature header?
- They are completely different tags despite sharing the letter. The s= tag in the DKIM-Signature header (RFC 6376 §3.5) is the SELECTOR name — it tells the verifier which DNS name to query (selector._domainkey.<d-value>). The s= tag in the DKIM KEY record (RFC 6376 §3.6.1, at <selector>._domainkey.<domain>) is the SERVICE TYPE list — it names which mail-related services the key is authorized for. The default s= value in the key record is "*" (any service); the most common explicit value is "email" (the only service type defined by RFC 6376 §3.6.1). Verifiers MUST ignore signatures from a key whose s= list does not include the relevant service type, but in practice s= is almost never restrictive because there is only one defined service type. So: header s= = selector name (mandatory, structural); key-record s= = service type (optional, almost always "*" or "email"). Use help_dkim_selectors_explained.php for the selector deep dive and help_dkim_signatures_explained.php for the header tag anatomy.
Validate your DKIM record now
DMARC Analyzer ingests every aggregate report and surfaces the per-selector pass/fail profile in the dashboard. Cross-check against the Domain Security Checker for the live DNS state of every selector your domain publishes.