> ## Documentation Index
> Fetch the complete documentation index at: https://docs.37audits.com/llms.txt
> Use this file to discover all available pages before exploring further.

# DNS Mail DMARC

> Checks the DMARC record that tells receivers what to do with email that fails SPF and DKIM for the domain, and where to send reports.

<Info>
  **Security Auditor** — looks up the TXT record at `_dmarc.<domain>` of the audited URL and checks that it is a single, well-formed DMARC record with an enforcing policy (`p=quarantine` or `p=reject`) and an aggregate-report address (`rua`).
</Info>

<CardGroup cols={2}>
  <Card title="Protection" icon="shield-halved">
    Tells receiving mail servers to quarantine or reject email that pretends to come from your domain but fails SPF and DKIM.
  </Card>

  <Card title="Standards" icon="book">
    Follows RFC 7489, Domain-based Message Authentication, Reporting, and Conformance (DMARC), March 2015.
  </Card>
</CardGroup>

SPF and DKIM let a receiver verify who really sent an email, but on their own they do not say what to do when the verification fails. DMARC closes that gap: a TXT record at `_dmarc.<domain>` publishes the domain owner's policy (`p=none`, `p=quarantine` or `p=reject`) and, through the `rua` tag, where receivers should send aggregate reports about mail that claims the domain. Without an enforcing policy, phishing sent in the company's name is still delivered; without reports, nobody notices. This auditor reads that record for the host of the audited URL.

## How it works

1. **Domain** — takes the host of the audited URL and strips one leading `www.`; `https://www.example.com/page` is audited as `example.com`. A URL that is empty, cannot be parsed or has no hostname ends the audit with `501`.
2. **Lookup** — queries the TXT records at `_dmarc.<domain>` through the system resolvers, with no cache and no organizational-domain fallback: `shop.example.com` is audited at `_dmarc.shop.example.com`, not at `_dmarc.example.com`. SERVFAIL, a timeout or any resolver failure ends the audit with `500`; NXDOMAIN and NODATA are "no record". This auditor never fetches the audited page.
3. **Select** — keeps the TXT values that start with `v=DMARC1` (case-insensitive). None at all is `400` when the name has no TXT record, or `401` when it has TXT records of another kind; more than one is `402` (RFC 7489 section 6.6.3: receivers then apply no policy).
4. **Rules on the record** — only when exactly one `v=DMARC1` record exists, its tags are parsed (`;`-separated `key=value`, keys case-insensitive). A missing or invalid `p` tag fails (`403`); `p=none` warns (`300`); a missing or blank `rua` warns (`301`).
5. **Aggregate** — FAIL wins over WARNING; when every rule passes the audit is `SUCCESS` (`200`) and the message names the policy, the `aspf` and `adkim` alignment modes (default `r`) and the `rua` and `ruf` addresses.
6. **Specification pin** — every run emits `37A-DnsMailDmarcAuditor-100` (`INFORMATION`), naming [RFC 7489, Domain-based Message Authentication, Reporting, and Conformance (DMARC)](https://www.rfc-editor.org/rfc/rfc7489) (March 2015). It does not change the overall status.

## What it audits

Every row maps to one `checkRule*` method and one `CheckCode`, in the order the auditor runs them. CheckCode suffixes use status ranges: `100` information, `200` success, `300–399` warning, `400–499` fail, `500–599` error. `{fqdn}` is `_dmarc.<domain>`, `{record}` the DMARC record text and `{url}` the audited URL.

| CheckCode | Status | When it fires | Recommendation | Why it matters |
| - | - | - | - | - |
| <a id="37A-DnsMailDmarcAuditor-100" />`37A-DnsMailDmarcAuditor-100` | `INFORMATION` | Always. Records the specification this auditor implements. | — (recommendation is `null`; nothing for the site to change) | — |
| <a id="37A-DnsMailDmarcAuditor-400" />`37A-DnsMailDmarcAuditor-400` | `FAIL` | `_dmarc.<domain>` has no TXT record at all (NXDOMAIN or NODATA). | Publish a TXT record at `{fqdn}` such as "v=DMARC1; p=reject; rua=mailto:\<reports mailbox>". | Without DMARC, receivers have no instruction to reject email that fails SPF and DKIM, so anyone can send phishing that appears to come from the domain and its reputation and deliverability suffer. |
| <a id="37A-DnsMailDmarcAuditor-401" />`37A-DnsMailDmarcAuditor-401` | `FAIL` | `_dmarc.<domain>` has TXT records but none starts with `v=DMARC1` (case-insensitive). | Start the DMARC record at `{fqdn}` with "v=DMARC1;" (found `{txtRecords}`). | Receivers ignore a DMARC record that does not start with the version tag, so the domain is as exposed to spoofing as if it published no record at all. |
| <a id="37A-DnsMailDmarcAuditor-402" />`37A-DnsMailDmarcAuditor-402` | `FAIL` | More than one `v=DMARC1` record exists at `_dmarc.<domain>`. | Keep a single v=DMARC1 TXT record at `{fqdn}` and delete the other `{count − 1}` (found `{records}`). | When several DMARC records are published, receivers discard all of them and apply no policy, so the protection the domain owner thinks is in place does not exist. |
| <a id="37A-DnsMailDmarcAuditor-403" />`37A-DnsMailDmarcAuditor-403` | `FAIL` | The single DMARC record has no `p` tag, or its value is not `none`, `quarantine` or `reject`. | Add p=reject (or p=quarantine) to the DMARC record at `{fqdn}` (currently "`{record}`"). | The p tag is the instruction receivers act on; without a valid one they treat the record as malformed and deliver spoofed mail as if no DMARC existed. |
| <a id="37A-DnsMailDmarcAuditor-300" />`37A-DnsMailDmarcAuditor-300` | `WARNING` | The single DMARC record has `p=none`. | Change p=none to p=quarantine, then p=reject, in the DMARC record at `{fqdn}` once the aggregate reports show your legitimate senders pass. | In monitoring mode receivers still deliver email that fails authentication, so the domain collects reports but phishing sent in its name reaches customers and partners unchallenged. |
| <a id="37A-DnsMailDmarcAuditor-301" />`37A-DnsMailDmarcAuditor-301` | `WARNING` | The single DMARC record has no `rua` tag, or its value is blank. | Add rua=mailto:\<reports mailbox> to the DMARC record at `{fqdn}` (currently "`{record}`") to receive aggregate reports. | Without aggregate reports nobody learns which senders fail authentication, so a misconfigured legitimate sender and an ongoing spoofing campaign both go unnoticed. |
| <a id="37A-DnsMailDmarcAuditor-200" />`37A-DnsMailDmarcAuditor-200` | `SUCCESS` | Exactly one valid record with `p=quarantine` or `p=reject` and no warning fired. Message names `p`, `aspf`, `adkim`, `rua`, `ruf` and the record. | — | — |
| <a id="37A-DnsMailDmarcAuditor-500" />`37A-DnsMailDmarcAuditor-500` | `ERROR` | The DNS lookup of `_dmarc.<domain>` failed (SERVFAIL, timeout, resolver error), so whether a record exists is unknown. | Check that the name servers of `{fqdn}` answer TXT queries and run the audit again. Last error was "`{error}`". | — |
| <a id="37A-DnsMailDmarcAuditor-501" />`37A-DnsMailDmarcAuditor-501` | `ERROR` | The audited URL is empty, cannot be parsed or has no hostname. | Provide a full URL with a hostname, such as [https://example.com/](https://example.com/), instead of "`{url}`" and run the audit again. | — |

## Output documentation

| Status | Description | Test logic |
| - | - | - |
| **SUCCESS** | The domain enforces DMARC | One `v=DMARC1` record at `_dmarc.<domain>` with `p=quarantine` or `p=reject` and a `rua` address (`200`). |
| **WARNING** | DMARC exists but does not protect or report fully | `p=none` (`300`); no `rua` tag (`301`). |
| **FAIL** | Receivers apply no DMARC policy | No TXT record (`400`); no `v=DMARC1` record (`401`); several `v=DMARC1` records (`402`); missing or invalid `p` tag (`403`). |
| **ERROR** | The auditor could not run | DNS lookup failed (`500`); invalid URL or no hostname (`501`). |
| **INFORMATION** | Specification pin | Always emitted (`100`); does not change overall status. |

Every check carries `domain`, `dmarcFqdn`, `txtRecords` (all TXT values found), `recordCount` (how many start with `v=DMARC1`), `dmarcRecord` (the single record, or `null`), `dmarcTags`, `policy`, `subdomainPolicy`, `pct`, `rua`, `ruf`, `aspf` and `adkim` (the last two default to `r`) in its `data`. The `500` check carries `domain`, `dmarcFqdn` and `error`; the `501` check carries `url` and `error`.

## Risks and considerations

### Why DMARC matters

* Email is the most common way into a company: an invoice, a password reset or a "message from the CEO" that only looks like it comes from your domain. SPF and DKIM let receivers detect the forgery; DMARC is what tells them to act on it.
* `p=none` is the right first step while you discover every legitimate sender from the reports, but it protects nobody. Move to `p=quarantine` and then `p=reject` once the reports are clean.
* The `rua` reports are the only feedback loop: without them, a newly added marketing tool that fails alignment, or a spoofing campaign against your customers, stays invisible.

### What this auditor does not check

* Only `_dmarc.<host>` of the audited URL is queried. RFC 7489 lets receivers fall back to the organizational domain's record for a subdomain (`sp` tag); this auditor does not, so a subdomain without its own record fails even when the parent domain enforces DMARC.
* The SPF and DKIM records themselves, the `pct`, `sp`, `fo` and `rf` tags, and whether the `rua` and `ruf` mailboxes accept reports (the external-destination verification of RFC 7489 section 7.1) are not evaluated.
* A record whose syntax is broken beyond the `v` and `p` tags is reported only through the tags this auditor reads.

### Changes from the previous version

* A missing or invalid `p` tag was a `WARNING` (it was read as `p=none`); it is now `FAIL` (`403`), as RFC 7489 requires the tag.
* A missing `rua` was only noted in the success message; it is now a `WARNING` (`301`).
* Several `v=DMARC1` records were silently reduced to the first one; they are now `FAIL` (`402`).
* An invalid URL or a URL without a hostname was `FAIL`; it is now `ERROR` (`501`), as the audit could not run.
* A failed DNS lookup (SERVFAIL, timeout) was reported as a missing record (`FAIL`); it is now `ERROR` (`500`), so a resolver problem is never charged as a finding against the domain.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.