--- title: "How to Configure SPF and DMARC Records for Your Domain" description: "Build and publish SPF and DMARC DNS records for your mail domain, verify them with dig, read DMARC reports and move safely from p=none to a reject policy." category: "Email Server" language: en source: https://cubepath.com/docs/email-server/spf-and-dmarc-configuration updated: 2026-09-25 --- # How to Configure SPF and DMARC Records for Your Domain SPF and DMARC are two DNS records that tell other mail servers how to treat messages claiming to come from your domain. SPF lists the servers allowed to send mail for the domain. DMARC ties SPF and DKIM to the visible `From` address, tells receivers what to do with mail that fails, and sends you reports about who is sending as your domain. In this tutorial you will inventory your mail sources, publish an SPF record and a DMARC record, verify both, and tighten the DMARC policy step by step until spoofed mail is rejected. ## Prerequisites To follow this tutorial, you need: - A domain you control, with access to its DNS zone. This guide uses `your_domain`; replace it with your own. - A mail server that sends for the domain, for example Postfix on a CubePath VPS running Ubuntu 24.04, with its public IP address. This guide uses `203.0.113.10` as `your_server_ip`. - DKIM signing already working, as described in [How to Set Up DKIM with OpenDKIM and Postfix](/docs/email-server/dkim-configuration-for-email-authentication). DMARC works with SPF alone, but forwarded mail usually breaks SPF, so DKIM is what keeps legitimate mail passing. - The `dig` command, provided by the `bind9-dnsutils` package on Ubuntu (`sudo apt install bind9-dnsutils`). ## How SPF, DKIM and DMARC fit together | Record | Where it lives | What it checks | |--------|----------------|----------------| | SPF | TXT on `your_domain` | The sending IP is authorized for the envelope sender domain (the `MAIL FROM`, also called Return-Path) | | DKIM | TXT on `selector._domainkey.your_domain` | The message carries a valid signature from the domain | | DMARC | TXT on `_dmarc.your_domain` | SPF or DKIM passed **and** the domain that passed matches the `From` header domain | The last point is called **alignment**, and it is what makes DMARC useful. A spammer can pass SPF for their own domain while putting your domain in the `From` header; DMARC fails that message because the domains do not match. A message passes DMARC when at least one of SPF or DKIM passes and is aligned. ## Step 1 - Listing everything that sends mail as your domain An SPF record that forgets a legitimate sender causes that sender's mail to fail. Before writing anything, list every system that sends with an `@your_domain` address: - Your own mail server (its IPv4 and, if it sends over IPv6, its IPv6 address). - A hosted mailbox provider such as Google Workspace or Microsoft 365. - Newsletter and transactional services (Mailchimp, Brevo, Amazon SES, Postmark and so on). - Web applications, monitoring tools, printers and anything else that sends notifications. Each provider documents the SPF `include:` value to use, and most also let you set up DKIM for your domain. Check whether an SPF record already exists, because a domain must have only one: ```bash dig +short TXT your_domain ``` ```output "google-site-verification=..." ``` If you see a string starting with `v=spf1`, edit that record in the next step instead of creating a second one. Two SPF records make SPF return `permerror` and fail for all mail. ## Step 2 - Building the SPF record An SPF record starts with `v=spf1`, lists the allowed senders as mechanisms, and ends with an `all` mechanism that says what to do with everything else. The mechanisms you will use most: | Mechanism | Meaning | |-----------|---------| | `ip4:203.0.113.10` | This IPv4 address (a CIDR range like `203.0.113.0/28` also works) | | `ip6:2001:db8::10` | This IPv6 address or range | | `mx` | The servers listed in the domain's MX records | | `a` | The IP addresses of the domain's A/AAAA record | | `include:_spf.google.com` | Everything the other domain's SPF record authorizes | | `-all` | Fail everything not listed | | `~all` | Soft fail everything not listed (accepted but treated as suspicious) | For a domain whose mail is sent only by your own server: ```text v=spf1 ip4:203.0.113.10 -all ``` For your own server plus Google Workspace and Amazon SES: ```text v=spf1 ip4:203.0.113.10 include:_spf.google.com include:amazonses.com -all ``` Keep these rules in mind: - **Ten DNS lookups at most.** Every `include`, `a`, `mx`, `exists` and `redirect` costs a lookup, including the ones nested inside included records. Past ten, SPF returns `permerror`. `ip4` and `ip6` cost nothing, so prefer them for your own servers. - **Use `-all` or `~all`**, never `+all`, which authorizes the whole Internet. With DMARC in place, `~all` is common and safe, because DMARC decides the final outcome. Use `-all` once you are sure the list is complete. - **Do not use `ptr`.** It is deprecated and many receivers ignore it. For domains and subdomains that never send mail, publish a record that authorizes nothing, so nobody can use them for spoofing: ```text v=spf1 -all ``` It is also good practice to give your server's hostname (the name it uses in `HELO`, such as `mail.your_domain`) its own record, because some receivers check SPF on the `HELO` name: ```text v=spf1 a -all ``` ## Step 3 - Publishing and verifying the SPF record In your DNS provider, create or edit the TXT record on the domain root: | Field | Value | |-------|-------| | Type | `TXT` | | Name / Host | `@` (or `your_domain`) | | Value | `v=spf1 ip4:203.0.113.10 -all` | Add the record for `mail.your_domain` the same way if you use one. After a few minutes, query it: ```bash dig +short TXT your_domain | grep spf1 ``` ```output "v=spf1 ip4:203.0.113.10 -all" ``` To see the result a receiver reaches, send a message from your server to a Gmail address and open **Show original**. The authentication summary should read: ```text SPF: PASS with IP 203.0.113.10 ``` ## Step 4 - Creating the DMARC record A DMARC record is a TXT record on `_dmarc.your_domain` made of `tag=value` pairs: | Tag | Meaning | |-----|---------| | `v=DMARC1` | Version, must come first | | `p=` | Policy for the domain: `none` (monitor only), `quarantine` (send to spam) or `reject` | | `rua=` | Address that receives daily aggregate reports, as a `mailto:` URI | | `sp=` | Policy for subdomains; defaults to the value of `p` | | `adkim=` / `aspf=` | Alignment mode: `r` (relaxed, the default: `mail.your_domain` aligns with `your_domain`) or `s` (strict, exact match) | Start with a monitoring policy. It changes nothing for delivery but gives you reports on every source that sends as your domain: ```text v=DMARC1; p=none; rua=mailto:dmarc-reports@your_domain ``` Create a mailbox or alias for `dmarc-reports@your_domain` first. Large receivers send one compressed XML report per day, and busy domains get many, so a dedicated address keeps them out of personal inboxes. > Note: If the reports go to an address on a different domain, that domain must authorize it by publishing a TXT record `your_domain._report._dmarc.other_domain` with the value `v=DMARC1`. Otherwise receivers will not send the reports there. ## Step 5 - Publishing and verifying the DMARC record Create the TXT record: | Field | Value | |-------|-------| | Type | `TXT` | | Name / Host | `_dmarc` (or `_dmarc.your_domain`) | | Value | `v=DMARC1; p=none; rua=mailto:dmarc-reports@your_domain` | Verify it: ```bash dig +short TXT _dmarc.your_domain ``` ```output "v=DMARC1; p=none; rua=mailto:dmarc-reports@your_domain" ``` Send another test message to Gmail and check **Show original**: ```text SPF: PASS with IP 203.0.113.10 DKIM: 'PASS' with domain your_domain DMARC: 'PASS' ``` ## Step 6 - Reading the aggregate reports After a day or two, reports start arriving as `.zip` or `.xml.gz` attachments. Each one lists, per sending IP, how many messages the receiver saw and whether SPF and DKIM passed and aligned. You can inspect one on the server with standard tools: ```bash zcat google.com!your_domain!1758758400!1758844799.xml.gz | less ``` The important part of each `` looks like this: ```xml 198.51.100.25 42 none fail fail ``` For each source IP that fails, decide whether it is legitimate: - **A service you use** (a CRM, a newsletter tool): add its `include:` to SPF and set up DKIM for your domain in that service. - **Forwarded mail**: SPF breaks on forwarding, but DKIM usually survives. If DKIM passes, there is nothing to fix. - **Unknown IPs**: most likely spoofing. This is exactly what the policy will block. Reading XML by hand does not scale. For regular use, send the reports to a DMARC analysis service or run an open source parser such as `parsedmarc`, which turns them into dashboards. ## Step 7 - Moving to quarantine and reject Once the reports show that all legitimate sources pass DMARC for a couple of weeks, tighten the policy. Change the record to quarantine, so failing mail goes to spam: ```text v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@your_domain ``` Watch the reports for another two to four weeks. If no legitimate mail fails, switch to reject, so failing mail is refused outright: ```text v=DMARC1; p=reject; rua=mailto:dmarc-reports@your_domain ``` Keep `rua` in place after reaching `reject`. The reports are how you notice a new service someone in your organization starts using without configuring SPF and DKIM. For domains that never send mail, you can publish the strict policy right away, together with `v=spf1 -all`: ```text v=DMARC1; p=reject ``` ## Troubleshooting - **`SPF: PERMERROR` in the headers.** The domain has two SPF records, or the record needs more than ten DNS lookups. Merge the records into one and replace `a`/`mx` lookups for your own servers with `ip4`/`ip6`. - **SPF passes but DMARC fails.** The envelope sender domain differs from the `From` domain, which is common with third-party senders that use their own bounce domain. Configure DKIM for your domain in that service, or set up its custom return-path option. - **No DMARC reports arrive.** Check the `rua` address for typos, confirm the mailbox receives external mail, and, if it is on another domain, publish the `_report._dmarc` authorization record. Remember that reports are sent once a day. - **Legitimate mail lands in spam after switching to `quarantine`.** Look up the source IP in the reports, fix its SPF or DKIM, and go back to `p=none` in the meantime if the impact is large. ## Conclusion Your domain now publishes an SPF record listing its legitimate senders and a DMARC policy that receivers enforce, with daily reports that show who sends as your domain. Together with DKIM, this is the authentication baseline that Gmail, Yahoo and Microsoft expect from senders. Next, make sure your server's reverse DNS matches its hostname and review the other deliverability factors in [How to Prevent Your Emails from Going to Spam](/docs/email-server/how-to-prevent-your-emails-from-going-to-spam).