# DMARC Record Checker

Fetch and validate the DMARC (RFC 7489, DMARCbis tags np/t/psd) record at _dmarc.<domain>. When a subdomain has no record, falls back to the organizational domain (Public Suffix List), as receivers do. Parses every tag (p, sp, np, pct, adkim, aspf, rua, ruf, fo, ri), reports the effective policy and its strength (monitoring, partial, enforcing), flags errors and weak settings, and verifies that report mailboxes in other domains are authorized by a <domain>._report._dmarc.<reporter> TXT record. A missing record is a normal result.

**Not for:** Generating a record (use dmarc_generate) or checking SPF (use spf_check).

- Tool id: `dmarc-checker`
- Category: email
- Execution: network
- MCP tool `dmarc_check` at https://lookupowl.com/mcp
- REST `GET https://lookupowl.com/api/v1/tools/dmarc-checker` (or POST a JSON body)
- OpenAPI: https://lookupowl.com/api/openapi.json

## Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `domain` | string | yes |  | Domain to check, e.g. example.com (not _dmarc.example.com, though that is accepted). A URL or e-mail address is reduced to its domain; Unicode names are converted to Punycode. For a subdomain without its own record the organizational domain's record is used, as receivers do. max length 253 |

## Output

Results are wrapped in an envelope: `tool`, `version`, `requestId`, `data`, `summary`, `meta` (cache status, timing), `permalink`. `data` has these fields:

| Field | Type | Description |
|---|---|---|
| `domain` | string |  |
| `status` | "ok" \| "warning" \| "error" \| "missing" \| "dns-error" | ok = valid, warning = valid with warnings, error = the record is invalid and receivers ignore it, missing = no DMARC record at the domain or its organizational domain, dns-error = the resolver returned SERVFAIL or REFUSED. |
| `valid` | boolean |  |
| `strength` | "none" \| "monitoring" \| "partial" \| "enforcing" | none = no usable policy, monitoring = p=none, partial = quarantine/reject for only part of the mail (pct below 100), enforcing = quarantine or reject. |
| `record` | string |  |
| `records` | string[] | Every TXT record at the DMARC name that starts with v=DMARC1 (more than one disables DMARC). |
| `host` | string | The DNS name the record was found at. |
| `policyDomain` | string | The domain that publishes the record. |
| `inherited` | boolean | The record belongs to the organizational domain, not to the domain asked about. |
| `queried` | string[] | DNS names asked for the policy, in order. |
| `policy` | object |  |
| `effective` | object | The policy that applies to mail from the domain asked about (sp for a subdomain using the parent's record). |
| `tags` | object[] |  |
| `destinations` | object[] |  |
| `issues` | object[] |  |
| `notes` | string[] |  |
| `dnsQueries` | integer | min -9007199254740991, max 9007199254740991 |
| `ttl` | integer | min -9007199254740991, max 9007199254740991 |

## Examples

### DMARC of google.com

```json
{"domain":"google.com"}
```

```bash
curl 'https://lookupowl.com/api/v1/tools/dmarc-checker?domain=google.com'
```

Web page: https://lookupowl.com/dmarc-checker?q=google.com

### DMARC of github.com

```json
{"domain":"github.com"}
```

```bash
curl 'https://lookupowl.com/api/v1/tools/dmarc-checker?domain=github.com'
```

Web page: https://lookupowl.com/dmarc-checker?q=github.com

### A subdomain that inherits its parent's policy

```json
{"domain":"mail.google.com"}
```

```bash
curl 'https://lookupowl.com/api/v1/tools/dmarc-checker?domain=mail.google.com'
```

Web page: https://lookupowl.com/dmarc-checker?q=mail.google.com

## Web pages

- https://lookupowl.com/dmarc-checker

## Limits and caching

- Cost: 3 units for a live lookup, 1 unit for a cached one. Budgets are per network and published in the `RateLimit` response headers.
- Cache: the lifetime follows the data (for DNS, the record TTL, within a few minutes). Add `fresh=true` (MCP argument or query parameter) to look again; it costs 3 units per unit and is limited to 10 per hour.
- Timeout: 10 s.

## Related tools

- [DMARC Record Generator](https://lookupowl.com/tools/dmarc-generator.md): Build a valid DMARC record, with report authorization records and a step-by-step rollout plan.
- [SPF Record Checker](https://lookupowl.com/tools/spf-checker.md): Check a domain's SPF record, count its DNS lookups against the limit of 10 and find errors.
- [DKIM Record Checker](https://lookupowl.com/tools/dkim-checker.md): Find and check a domain's DKIM public keys, even when you do not know the selector.
- [IP and Domain Blacklist Checker](https://lookupowl.com/tools/blacklist-checker.md): Check an IP address, a mail server or a domain against live spam blocklists, with how to get delisted.
- [SPF Record Generator](https://lookupowl.com/tools/spf-generator.md): Build a valid SPF record from your mail providers and IP addresses, with a lookup-limit check.
- [DNS Lookup](https://lookupowl.com/tools/dns-lookup.md): Look up A, AAAA, MX, NS, TXT, CNAME, SOA, CAA, DS and DNSKEY records from a public resolver or the authoritative nameserver.

## Guide: DMARC checker

Read a domain's DMARC policy and what it asks mail servers to do with fakes.

### What the DMARC checker shows

Enter a domain and the checker finds its DMARC record, reads the policy and tells you in plain words how well the domain is protected against forged mail. It lists every tag, shows where reports are sent and flags mistakes that make receivers ignore the record.

DMARC (Domain-based Message Authentication, Reporting and Conformance) is a DNS record that tells receiving mail servers what to do with a message that claims to come from your domain but fails authentication. It also says where to send reports about those messages. A message passes when SPF or DKIM passes **and** the domain that passed matches the domain in the visible From address. That match is called alignment.

The record lives at `_dmarc.<domain>`. A subdomain without a record of its own uses the one at its organizational domain, which is the domain you registered. For `mail.example.com` that is `example.com`. The result tells you when a policy is inherited this way.

### How to check a domain

1. **Enter the domain**: Use the domain in your From address, such as `example.com`. If you paste an email address, a web address or the record name `_dmarc.example.com`, LookupOwl reduces it to the domain.
2. **Read the answer**: The top tile gives the verdict, from no record at all to enforced. Beside it you see the policy, the share of failing mail it applies to, where reports go and what subdomains do.
3. **Work through the notes**: Notes come with errors first, then warnings, then hints. Fix the errors first, because they can make receivers ignore the record.

> **No record yet?** Build one with the [DMARC record generator](https://lookupowl.com/dmarc-record-generator). It also gives you a staged plan from monitoring to enforcement.

> **In Claude, ChatGPT or Cursor** Connect the LookupOwl MCP server and ask: “Is the DMARC policy for example.com enforcing?” The assistant calls `dmarc_check` and reads the same structured answer you see here.

### The DMARC tags and what they mean

| Tag | Meaning | Example |
|---|---|---|
| `v` | Version. Must come first and read exactly `DMARC1`. | `v=DMARC1` |
| `p` | What receivers do with mail that fails: `none` (only report), `quarantine` (spam folder) or `reject` (refuse). Always include it: the checker reports a record without `p` as invalid. | `p=reject` |
| `sp` | Policy for subdomains. Follows `p` when left out. | `sp=quarantine` |
| `np` | Policy for subdomains that do not exist. New in DMARCbis. Follows `sp`, then `p`. | `np=reject` |
| `rua` | Where daily summary (aggregate) reports go. Separate several `mailto:` addresses with commas. | `rua=mailto:dmarc@example.com` |
| `ruf` | Where individual failure reports go. Most large providers do not send them. | `ruf=mailto:forensic@example.com` |
| `adkim` / `aspf` | How closely the DKIM or SPF domain must match the From domain: `r` relaxed (any subdomain of the same organizational domain matches, the default) or `s` strict (exact match). | `adkim=s` |
| `fo` | When failure reports are wanted, if `ruf` is set. `0` when all checks fail (default), `1` when any fails, `d` for DKIM, `s` for SPF. Join with colons. | `fo=1` |
| `t` | Testing flag, new in DMARCbis. `t=y` asks receivers to go one step easier on failing mail while you test. | `t=y` |
| `pct` | Share of failing mail the policy applies to, from 0 to 100. Removed in DMARCbis, but large providers still honour it. | `pct=25` |

The checker also reads `psd` (new in DMARCbis, for public suffix domains) and the old `ri` and `rf` tags. DMARCbis dropped `pct`, `ri` and `rf`, so those three show up as hints, not errors.

The result gives the policy a plain label. **None** means no usable policy: there is no record, it is invalid or there are two. **Monitoring** means `p=none`. **Partial** means `quarantine` or `reject` for only part of the mail because of `pct`. **Enforcing** means `quarantine` or `reject` for all of it. For a subdomain that inherits its parent's record, the label follows the parent's `sp` tag when it has one.

Alignment trips up many senders. If your newsletter service sends as `news@example.com` but signs with its own domain, SPF and DKIM can pass for the service while DMARC fails for you. The fix is to have the service sign with a DKIM key for your own domain, or to use your domain as its bounce address (the return path, which is what SPF checks).

### Problems the checker flags

- **No DMARC record.** Receivers have no policy to apply, and Gmail, Yahoo and Microsoft expect DMARC from bulk senders. Start with `v=DMARC1; p=none; rua=mailto:you@example.com`.
- **Stuck on `p=none`.** Monitoring is useful for a few weeks, but it does not stop spoofing. Move to `quarantine` and then `reject` once your real senders pass.
- **No report address.** `p=none` without `rua` is a warning, because you would be monitoring with nothing to read. With `quarantine` or `reject` it is a hint.
- **Two DMARC records.** Receivers discard both, so the name has no working policy. Keep exactly one TXT record at `_dmarc`.
- **Invalid syntax.** Tags are separated by semicolons. A missing `;`, a report address without `mailto:`, quotes around the address or a value such as `p=block` make the record invalid. The checker names the tag.
- **Subdomain policy weaker than `p`.** If `sp` or `np` is softer than `p`, forged mail from subdomains is treated more gently, and attackers look for exactly that gap.
- **Report address not authorised.** If `rua` or `ruf` points to a mailbox on a different domain, that domain must publish a TXT record at `example.com._report._dmarc.otherdomain` with the value `v=DMARC1`. Without it, receivers do not send the reports. The checker looks for it, for up to five outside domains, and warns when it is missing. Reporting services normally publish it for you.

The checker reads DNS, not your mail, so it cannot tell whether your real messages pass. Two things catch people out. Services that sign only with their own domain fail alignment for you. And forwarding often breaks SPF, because the message arrives from the forwarder's address, while mailing lists can break DKIM by changing the message. Read your aggregate reports for both before you enforce.

### How the check works

LookupOwl asks DNS for the TXT records at `_dmarc.<domain>` and keeps those that start with `v=DMARC1`. If there are none and the name is a subdomain, it asks once more at the organizational domain, which it works out from the Public Suffix List. That is the method of RFC 7489. DMARCbis (RFC 9989) replaces it with a DNS tree walk that climbs the name one label at a time. In most cases both end at the same record, but they can differ when a domain publishes records at several levels.

A temporary DNS failure is reported as one, not as a missing record, and the checker does not fall back to a weaker parent policy. Results are kept for up to five minutes, so use Check again after you change the record. To read the record yourself:

```bash
dig +short TXT _dmarc.example.com
nslookup -type=TXT _dmarc.example.com
```

### Standards

- **RFC 9989**: DMARCbis, published in May 2026. It replaces RFC 7489 and RFC 9091, adds `np`, `psd` and `t` and the DNS tree walk, and drops `pct`, `rf` and `ri`. The record still begins `v=DMARC1`.
- **RFC 9990** and **RFC 9991**: the formats of aggregate reports and failure reports.
- **RFC 7489**: the 2015 specification that DMARCbis replaces. Many receivers still follow it, which is why the checker uses its way of finding the policy and still understands its tags.
- **RFC 7208**: SPF. **RFC 6376**: DKIM.

Background reading: [SPF, DKIM and DMARC explained](https://lookupowl.com/learn/spf-dkim-dmarc-explained).

### Frequently asked questions

**What does p=none mean in DMARC?**

It means monitor only. Receivers deliver mail that fails DMARC as usual and, if you set `rua`, send you reports about it. That makes it the right first step, because it shows every system that sends as your domain without risking real mail. It does not stop anyone from forging your domain, so plan to move on to `quarantine` and then `reject`.

**Is DMARC required?**

For bulk senders, yes. The sender rules of Gmail, Yahoo and Microsoft ask for a DMARC record, at least `p=none`, plus aligned SPF or DKIM, from anyone sending roughly 5,000 or more messages a day to their consumer mailboxes. Below that, nobody forces you. Any domain that sends email still benefits, because without DMARC anyone can forge your From address and you never find out.

**Will DMARC block my legitimate email?**

Only mail that fails both aligned SPF and aligned DKIM, and only once your policy is `quarantine` or `reject`. With `p=none` nothing is blocked. That is why you read the reports first and fix every real sender, including newsletter tools, billing systems and mailing lists, before you tighten the policy.

**Do I need DMARC on a domain that never sends mail?**

Yes, and it takes two records. Publish `v=DMARC1; p=reject` at `_dmarc` and `v=spf1 -all` at the domain itself. Together they tell receivers that no mail should come from it, which makes a parked domain much harder to forge. If the domain should not receive mail either, add a null MX record (`0 .`) too.

**What are DMARC aggregate reports?**

They are summaries that receivers email to your `rua` address, usually once a day. Each one lists the IP addresses that sent mail claiming to be your domain, how many messages they sent and whether SPF, DKIM and DMARC passed. They are compressed XML, hard to read by hand, so most people send them to a DMARC reporting service. RFC 9990 defines the format.

**Why does the checker show a policy for a subdomain that has no record?**

Because receivers use the organizational domain's record when a subdomain has none, and the checker does the same. The result marks the policy as inherited and applies the parent's `sp` tag if it has one. If the subdomain needs its own policy, publish a record at `_dmarc.` followed by the subdomain, such as `_dmarc.mail.example.com`.

**Should every domain end at p=reject?**

Not always. A domain that only sends mail you control, such as a company or a brand, should aim for `reject`. If people use the domain for personal mail and post to mailing lists, `reject` can bounce their messages, and RFC 9989 advises against it for such general-purpose domains. For them, `quarantine` is the safer stopping point.
