# How to set up SPF, DKIM and DMARC (with Cloudflare and registro.br)

> What SPF, DKIM and DMARC are, the right record for each and the step by step on Cloudflare, registro.br and hPanel, with the most common mistakes and how to check that it works.

Source: https://couryo.com/en/guides/spf-dkim-dmarc · Updated 2026-10-08

SPF, DKIM and DMARC are three records in your domain's DNS. Together they tell email providers who may send on your behalf and what to do with anyone pretending to be you. Without them, your email is much more likely to [land in spam](https://couryo.com/en/guides/emails-going-to-spam.md) or be refused.

## The three in one sentence each

| Record | Where it lives | What it does |
|---|---|---|
| **SPF** | TXT on the envelope domain (or subdomain) | lists the servers allowed to send for that domain |
| **DKIM** | TXT or CNAME at `<selector>._domainkey.yourdomain` | publishes the key that checks each message's signature |
| **DMARC** | TXT at `_dmarc.yourdomain` | requires SPF or DKIM to be **aligned** with the `From` domain and says what to do when they are not |

**Alignment** is the key word. DMARC passes when at least one of these holds:

- DKIM passed **and** the signing domain (`d=`) is the `From` domain or a subdomain of it;
- SPF passed **and** the envelope domain (the "return path") is the `From` domain or a subdomain of it.

## SPF

An example for a company that uses Google Workspace for its mailboxes plus a sending service:

```text title="TXT on example.com"
v=spf1 include:_spf.google.com include:spf.sending-service.com ~all
```

Rules that save headaches:

- **One SPF record per name.** Two `v=spf1` records on the same domain invalidate both. Merge them into one.
- **At most 10 DNS lookups.** Every `include`, `a`, `mx` and `redirect` counts, including those inside the includes. Above 10, SPF fails with `permerror`.
- **End with `~all` or `-all`.** Never `+all`: it authorizes the whole world.
- SPF is checked on the **envelope domain**, not the `From`. Services that use a subdomain of yours as the return path (for example, `bounces.example.com`) take SPF there and do not need changes to your root domain's SPF.

## DKIM

The sending service generates the key and tells you which record to create. It comes in one of two forms:

```text title="TXT (the key published directly)"
service1._domainkey.example.com   TXT   "v=DKIM1; k=rsa; p=MIIBIjANBgkqh..."
```

```text title="CNAME (the service keeps the key and can rotate it without you)"
service1._domainkey.example.com   CNAME   service1.keys.sending-service.com
```

- The part before `._domainkey` is the **selector**. Each service uses its own; several can live on the same domain.
- Prefer **2048-bit** keys. Long keys in TXT sometimes need splitting into chunks of up to 255 characters; most panels do it for you.
- Make sure the service signs with **your domain** (`d=example.com`), not only its own. Without that, DMARC does not align.

## DMARC

Start by observing:

```text title="TXT on _dmarc.example.com"
v=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=r; aspf=r
```

| Part | Meaning |
|---|---|
| `p=none` | observe and send reports only; nothing is blocked |
| `p=quarantine` | failing messages go to spam |
| `p=reject` | failing messages are refused |
| `rua=mailto:...` | where providers send aggregate reports (daily XML) |
| `adkim=r`, `aspf=r` | relaxed alignment: subdomains count as the same domain |
| `pct=25` | optional: applies the policy to only part of the messages, to step up slowly |

Move from `none` to `quarantine` and then to `reject` only when the reports show that **every** service sending for your domain (company mailboxes, billing, CRM, your app) passes aligned. Moving too early makes legitimate email disappear.

## Step by step on Cloudflare

1. Sign in, pick the domain and open **DNS > Records**.
2. Click **Add record**.
3. Choose the **Type** (`TXT` or `CNAME`).
4. In **Name**, write only the part before the domain: `@` for the root, `_dmarc`, `service1._domainkey` or `bounces`. Cloudflare fills in the rest.
5. In **Content** (or **Target**, for CNAME), paste the value.
6. **For email CNAMEs, set Proxy status to "DNS only" (grey cloud).** With the orange cloud, the record answers with Cloudflare's IPs and DKIM and the return path break.
7. Save. On Cloudflare the change usually shows up within minutes.

## Step by step on registro.br (.br domains)

First, find out **who answers for the domain's DNS**. On registro.br, open the domain and look at its DNS servers:

- **If it uses registro.br's servers**, edit the zone there: open the domain, go to **DNS > Edit zone** (turn on advanced mode if the panel asks) and use **New entry** for each record.
- **If the servers are elsewhere** (Cloudflare, your host), editing on registro.br changes nothing: create the records in that other panel.

When creating the entry on registro.br:

| Field | Example |
|---|---|
| Name | only the part before the domain: `_dmarc`, `service1._domainkey`, `bounces` (empty for the root) |
| Type | `TXT` or `CNAME` |
| Data | the record's value |

Save the zone. Propagation can take from a few minutes to a few hours.

## Step by step on hPanel (hosting panel)

1. In **Domains**, pick the domain and open **DNS / Nameservers**.
2. In the record list, check what **already exists**: hosting panels often create an SPF (and sometimes a DMARC) for the host's own mailboxes.
3. **If an SPF exists, edit it and add the new `include`** instead of creating another record. Same for DMARC: one per domain.
4. For each new record, choose the **Type**, fill **Name** with only the part before the domain (`@` for the root) and the value in **Points to** (CNAME) or **TXT value** (TXT).
5. Save and wait for propagation.

## Check that it works

In a terminal:

```bash title="Terminal"
dig +short TXT example.com                    # SPF
dig +short TXT _dmarc.example.com             # DMARC
dig +short CNAME service1._domainkey.example.com
```

On Windows, `nslookup -type=TXT _dmarc.example.com` does the same. Then send an email to a Gmail account, open **Show original** and look for `SPF: PASS`, `DKIM: PASS` and `DMARC: PASS`.

## Most common mistakes

| Mistake | How it shows | Fix |
|---|---|---|
| Two SPF records | `spf=permerror` | merge into one record |
| More than 10 SPF lookups | `spf=permerror` | remove includes you no longer use |
| Name with the domain repeated | record at `_dmarc.example.com.example.com` | write only `_dmarc` in the name field |
| Proxied CNAME on Cloudflare | DKIM not found | switch to "DNS only" |
| Two DMARC records | DMARC ignored | delete one |
| DKIM from the service, not your domain | `dkim=pass` but `dmarc=fail` | set up the custom signing domain in the service |
| `p=reject` too early | billing or CRM email disappears | go back to `p=none`, fix the services and step up again |

## Where Couryo fits

With Couryo you do not touch your root domain's SPF: the envelope uses a subdomain of yours (`bounces.`), DKIM is published by CNAME (the key can be rotated without you touching DNS) and DMARC starts at `p=none`. With the domain on Cloudflare, the records are created for you. Then the deliverability agent reads the reports and tells you when it is safe to move to `quarantine` and `reject`, with the record ready. Details in [Domains and DNS](https://couryo.com/en/docs/domains.md).

## FAQ

### Do I need all three, or is one enough?

All three. Gmail and Yahoo require SPF or DKIM from every sender, and SPF, DKIM and DMARC from senders of more than 5,000 a day; Microsoft requires all three from bulk senders. Even below that, the three together weigh the most for reaching the inbox.

### Can I have more than one DKIM?

Yes, and it is normal: each service uses a different selector (`google._domainkey`, `service1._domainkey`...). What you cannot have is more than one SPF or more than one DMARC on the same name.

### How long does DNS take to propagate?

It depends on the record's TTL and the panel. On Cloudflare it is usually minutes; elsewhere, from minutes to a few hours. If the record still does not show in `dig` after a day, check that you edited the right panel (the one for the domain's DNS servers).

### What goes in DMARC's rua?

An address that receives the providers' daily XML reports. Since they are hard to read by hand, the usual choice is a service that collects and explains them.

### SPF with ~all or -all?

Both work. With DMARC in place, DMARC makes the final call, so `~all` is the safer choice while you have not checked every service that sends for the domain.
