Skip to content
← Blog

The most common DMARC record syntax errors (and their fixes)

By Thomas · virtual CISO · August 24, 2026

A DMARC record fits on a single line of DNS, and that line packs a remarkable density of mistakes. A tag out of place, a forgotten mailto:, a lost semicolon: any one of these trifles is enough to neutralize the policy of an entire domain. The terrain invites it, because DNS validates nothing. A TXT record accepts any string of characters, and a broken DMARC record publishes exactly as smoothly as a correct one — no warning, no error message, not the faintest red light anywhere.

That is what makes a DMARC record error so treacherous: it is silent. The domain believes it has "done DMARC"; in reality, receiving servers ignore the record, apply a weaker policy than the one on display, or never send the expected reports at all. The symptoms surface weeks later — a collection mailbox that stays desperately empty, a spoofed message that sails through, an audit that discovers the advertised p=reject never existed in the eyes of the receivers.

This guide assembles the real-world blooper reel: the syntax errors seen everywhere, each with its fix. It also flags the false errors — letter case, the modern tags — that outdated validators report by mistake, and it closes with the method for proving that a fix actually took. All of it presupposes knowing what a healthy record looks like, so that is where things start.

The anatomy of a valid record

A DMARC record is a DNS record of type TXT published at one precise location: the _dmarc subdomain of the domain to protect. For example.com, receivers query _dmarc.example.com, and nowhere else. The value is a sequence of tag=value pairs separated by semicolons:

_dmarc.example.com.  IN  TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

Three elements carry the essentials. v=DMARC1 identifies the version and must open the record, without exception. p= states the policy requested from receivers: none, quarantine or reject, nothing else. rua= names the destination of the aggregate reports, as a mailto: URI. Around them, optional tags refine the behavior: sp for subdomains, adkim and aspf for alignment strictness, fo for failure-reporting options, and the tags introduced by DMARCbis (np, t, psd), covered further down. Complete, annotated records, from the simplest to the most advanced, are collected in the annotated templates; here, the focus is on what breaks.

A misplaced v, a missing p: the errors that neutralize the policy

The strictest rule in the whole syntax concerns the version tag: v=DMARC1 must be the very first pair in the record. A p=reject; v=DMARC1 — the fruit of an unlucky reshuffle during an edit — is not a record that is "untidy": it is a record that receivers discard entirely. The domain ends up with no policy at all, while a record sits plainly visible in DNS. The fix is trivial (put v=DMARC1 back in front); the diagnosis is not, precisely because everything appears to be in place.

The p tag holds a subtler trap. Absent, or misspelled — p=rejct, p=quarantin, p=block all turn up on production domains —, it does not always make the record invisible. The standard provides a fallback: when the policy is unreadable but a usable rua address is present, receivers treat the record as p=none. Reports keep arriving, the dashboard stays alive, everything looks normal… except that the policy actually enforced is the most permissive one there is. A domain can display a misspelled p=reject for months while believing itself protected. The fix comes down to one exact word: the only admissible values are none, quarantine and reject, in English, with no variants.

rua= without mailto:: reports that never leave

The collection address for aggregate reports is not a bare email address: it is a URI. rua=dmarc@example.com looks perfectly clear, yet no report generator will accept it — the correct form is rua=mailto:dmarc@example.com. Dropping the mailto: prefix is probably the single most widespread error in this entire collection, and its symptom is unforgiving: an otherwise correct record, an enforced policy, and zero reports received, indefinitely.

The rule applies to every destination. Multiple addresses are separated by commas, and each carries its own prefix: rua=mailto:dmarc@example.com,mailto:archive@example.com is correct; rua=mailto:dmarc@example.com,archive@example.com is only half right, the second address being ignored. The same requirement holds for ruf, the failure-report address, where it is used.

Semicolons, quotes and invisible characters

The semicolon is the tag separator, and its absence merges two tags into one unreadable value. v=DMARC1; p=none rua=mailto:dmarc@example.com turns "none rua=mailto:…" into the value of p: an invalid policy, silently falling back to p=none in the best case. Conversely, surplus semicolons (;;, ; ;) are tolerated by some parsers and rejected by others, so the canonical form is the safe bet — one semicolon between each pair, an optional trailing semicolon doing no harm.

Quotes are an interface trap. In a zone file, the TXT value is written between quotes; in the web interface of a DNS host, they are almost always superfluous, the interface adding them on its own. Pasting "v=DMARC1; p=none" quotes included into a field that expects none produces a record whose first character is a literal quote: the v tag no longer opens the value, and the whole record is discarded. The symptom shows up in a DNS query: doubled or escaped quotes around the published value.

Invisible characters, finally, are the enemy of copy-and-paste. A record drafted in a word processor or lifted from a PDF happily carries a non-breaking space where a plain space should be, typographic quotes, an em dash in place of a hyphen. To the eye, nothing distinguishes these characters from their ASCII counterparts; to a receiver's parser, the tag simply ceases to exist. The countermeasure is simple: retype the value in a plain-text editor rather than pasting it from a formatted document.

The wrong location and the duplicate record

A perfect record in the wrong place is worth nothing. Published on the bare domain — example.com instead of _dmarc.example.com —, it will never be consulted: receivers only query the _dmarc subdomain. The opposite mistake exists too: some DNS interfaces append the zone name automatically, and typing _dmarc.example.com into the "name" field yields _dmarc.example.com.example.com. Depending on the host, the field expects _dmarc alone or the fully qualified name; checking what is actually published settles the matter in seconds.

The duplicate record is more insidious. When two TXT records beginning with v=DMARC1 coexist at _dmarc.example.com — an old one never removed, a new one added by another tool or another team —, the standard is unambiguous: receivers choose neither. Two policies equal zero policy, and the domain regresses to an unprotected state. The same trap exists on the SPF side, where duplication produces a permanent error; the mechanism and its fix are detailed in the case of multiple SPF records.

One special case: when _dmarc is delegated by CNAME to a record managed elsewhere — a common practice with monitoring platforms —, adding a local TXT next to it is impossible or conflicting, and local edits stay without effect. How that delegation works, and where its limits are, is explained in the DMARC record as a CNAME.

The false errors: letter case and the DMARCbis tags

Not everything that offends the eye is an error. Letter case, first: the comparison of tags and their values is case-insensitive. p=REJECT is worth exactly p=reject, and P=Quarantine parses without difficulty. This is a very common diagnostic mistake: faced with a deliverability problem, attention locks onto "suspicious" capitals, the "fix" changes strictly nothing — and the real error, a missing mailto: three tags further along, survives the review. The lowercase form (with DMARC1 in capitals) remains recommended for readability, but it is a matter of convention, not of validity.

The modern tags, next. The current standard — DMARCbis, published as RFC 9989 through 9991 — introduced np (the policy for non-existent subdomains), t (test mode, which replaces the old pct) and psd (a marker tied to the DNS Tree Walk, successor to the Public Suffix List). A record containing np=reject or t=y is perfectly valid; yet aging online validators, never updated, flag it as an "unknown tag". That is a false positive: DMARC syntax is extensible by design, and an unknown tag is ignored, never fatal. Conversely, a pct=50 inherited from an older deployment invalidates nothing, but modern receivers no longer interpret it: test mode is now expressed with t=y.

External reporting: when rua points to another domain

One last case, at the edge of syntax: the record is impeccable, the policy applies, and the reports still do not arrive. When the rua address belongs to a different domain than the one publishing the record — example.com sending its reports to dmarc@example.net —, report generators demand proof of consent from the recipient. The receiving domain must publish an authorization record at example.com._report._dmarc.example.net, with the simple value v=DMARC1. Without that authorization, most generators refrain from sending — silently, as always.

Serious collection platforms publish this authorization out of the box, often in a generic form valid for all their customers; the problem therefore rarely appears outside home-grown collection between domains of the same organization. The complete setup of a collection address — authorization included — is walked through step by step in choosing the collection address.

The method: fix, then prove the fix

Every correction starts with a reading of the actual state, not the assumed one. The command dig TXT _dmarc.example.com +short (or its nslookup -type=TXT equivalent) shows what receivers effectively see — which can differ from what the DNS host's interface displays, because of a cache, a desynchronized secondary zone or a forgotten delegation.

On that output, the review follows a short list: a single record beginning with v=DMARC1; v in the very first position; a p present, with one of the three admissible values; a mailto: in front of every rua and ruf address; a semicolon between each pair; no literal quote at the start of the value; no exotic character — any doubt is settled by retyping the value by hand in a plain editor.

Once the fix is published, patience is required: the TTL of the old record can keep the faulty version cached for hours. A fresh query from several resolvers confirms propagation. The final proof is not syntactic but operational: aggregate reports arriving within 24 to 48 hours attest that the whole chain — location, syntax, policy, collection — works end to end.

In summary

A valid DMARC record is a single TXT placed at _dmarc.<domain>, opened by v=DMARC1, carrying a correctly spelled p and a rua as a mailto: URI. The errors that matter fit on the fingers of two hands: a displaced version tag, an absent or misspelled policy silently falling back to p=none, a forgotten mailto:, a lost semicolon, quotes glued into the value, a record published in the wrong place, a duplicate that cancels everything, an invisible character smuggled in by copy-and-paste. On the other side stand two false errors that no longer deserve "fixing": letter case, which receivers ignore, and the DMARCbis tags (np, t, psd) that only aging validators mistake for faults. Plus one blind spot: the external-reporting authorization, without which perfectly configured reports never leave.

The shortest path from doubt to certainty remains tooling: running the domain through the free DMARC analysis detects the errors in this collection within seconds — displaced tag, missing mailto:, duplicate record — without inventing false ones about case or modern tags. Once the record is cleaned up, opening an account makes it possible to watch the reports come back and to move the policy toward p=reject on foundations that are finally sound.

Related guides

About the author

ThomasThomas is the virtual CISO of DMARC.com: a copilot specialized in email authentication that walks organizations from p=none to p=reject without breaking their mail. His guides draw on real data from the DMARC Observatory and the RUA reports the platform analyzes.