Email domain verification failed: how to fix it by DNS host
DKIM or SPF record not verifying? The 6 mistakes that block domain verification, how to spot each with dig, and the fix on Cloudflare, GoDaddy and more.
How-to7 min read
View as MarkdownYou copied the records from your email provider, pasted them into your DNS panel, waited a day, and the dashboard still says "pending". The deploy is done, the welcome email can't go out, and support keeps saying "wait for propagation". Most of the time propagation isn't the problem. It's one of the six mistakes below, and two commands tell you which one.
First: find out who answers for your domain's DNS#
Before touching any record, make sure you are editing the right panel. A domain's DNS is answered by the nameservers (NS) set at the registrar. Records created anywhere else change nothing.
dig +short NS example.com
# ana.ns.cloudflare.com.
# bob.ns.cloudflare.com.| NS ends in | Where to edit |
|---|---|
ns.cloudflare.com |
Cloudflare |
domaincontrol.com |
GoDaddy |
registrar-servers.com |
Namecheap |
vercel-dns.com |
Vercel |
dns-parking.com |
Hostinger (hPanel) |
That's what happened in a 2024 Stack Overflow question: the domain was registered in one place and the site hosted on Vercel, and the records were added at the registrar, then at Vercel, then at both. Two of three records verified; DKIM didn't. Running dig NS first ends the "where do these go?" guessing.
How to check a record with dig and nslookup#
The commands that answer "does the record exist, with the right value?":
dig +short TXT example.com # SPF
dig +short TXT _dmarc.example.com # DMARC
dig +short CNAME couryo1._domainkey.example.com # DKIM via CNAME
dig +short CNAME bounces.example.com # return pathnslookup -type=TXT _dmarc.example.com
nslookup -type=CNAME couryo1._domainkey.example.com
Resolve-DnsName -Type CNAME couryo1._domainkey.example.comTo take caches out of the picture, ask the domain's own nameserver (the one from dig NS):
dig +short CNAME couryo1._domainkey.example.com @ana.ns.cloudflare.comHow to read the result:
- Empty from the domain's nameserver: the record doesn't exist under that name. It's a name or panel mistake, not propagation.
- Correct from the domain's nameserver, empty from your machine: it's cache. Wait for the TTL or try another resolver (
@1.1.1.1,@8.8.8.8). - A different value: a typo, or an old record on the same name.
The 6 most common mistakes#
1. The domain is duplicated in the name field#
Nearly every panel appends your domain to the name automatically. Paste the full name and the record ends up at couryo1._domainkey.example.com.example.com.
How to spot it: dig on the right name is empty; on the duplicated name, it answers.
dig +short CNAME couryo1._domainkey.example.com.example.comThe fix: in the name field, type only the part before your domain (couryo1._domainkey, _dmarc, bounces). Some panels accept the full name with a trailing dot (couryo1._domainkey.example.com.), which marks it as absolute; when in doubt, use the short form.
2. A proxied CNAME on Cloudflare#
With the orange cloud on, Cloudflare answers with its own IPs instead of the CNAME, so DKIM and the return path no longer point to your email provider. Cloudflare's docs recommend DNS only for records used for email and third-party verification, and note that only A, AAAA and CNAME records can be proxied.
How to spot it: dig CNAME comes back empty, while dig A on the same name returns IPs.
The fix: edit the record and set Proxy status to DNS only (grey cloud).
3. Two SPF records#
Two TXT records starting with v=spf1 on the same name invalidate each other: the result is permerror (RFC 7208, section 4.5). It happens when your mailbox provider or forwarding service already created an SPF record and the new sender asks for another.
That was the case in a 2024 thread on the Vercel community: verification kept failing because of a conflict between the email forwarding service's SPF and the sending service's, and merging them into one record fixed it.
How to spot it:
dig +short TXT example.com | grep spf1The fix: one record, with every include and a single all at the end:
v=spf1 include:_spf.google.com include:spf.other-service.com ~allAlso keep the whole thing within 10 DNS lookups. See How to set up SPF, DKIM and DMARC.
4. Propagation and TTL (including cached "does not exist")#
Propagation is the time resolvers take to forget the old answer, and it depends on the record's TTL. A detail few people know: if the verifier looked up the name before you created the record, the "does not exist" answer is cached too, for a time set by the zone's SOA record (RFC 2308).
dig SOA example.com +noall +answer
# "does not exist" is cached for the lower of the SOA's TTL and its last number, in secondsThe fix: check against the domain's nameserver (with @). If it's right there, just wait. If your panel allows it, lower the TTL before a big change.
5. The record is in the wrong zone#
Variations of the same mistake:
- the record was created at the registrar, but DNS is served somewhere else (see
dig NSabove); - a subdomain has its own zone (for example,
mail.example.comdelegated to another provider) and the record went into the parent zone, or the other way around; - the record went into a similar domain in the same account (
example.coinstead ofexample.com).
How to spot it: dig +short NS mail.example.com shows whether the subdomain has its own nameservers.
6. Typos, whitespace and quotes#
- A space or line break at the start or end of the value, common when copying from a PDF or spreadsheet.
- Doubled quotes: some panels add the TXT quotes for you; pasting
"v=DMARC1; ..."with quotes can produce"\"v=DMARC1...". - A truncated DKIM key in long TXT records. Prefer CNAME-based DKIM when the provider offers it.
- A swapped letter in the selector (
couryo1versuscouyro1).
How to spot it: compare the dig output with the expected value character by character. Many providers show the value they found next to the expected one; use it.
Panel by panel#
| DNS host | Where | Name field | Watch out for |
|---|---|---|---|
| Cloudflare | domain > DNS > Records > Add record | short form; @ for the root |
grey cloud (DNS only) on email CNAMEs |
| GoDaddy | Domain Portfolio > domain > DNS > Add New Record (help) | @ or the prefix only |
the name can't start or end with a period |
| Namecheap | Domain List > Manage > Advanced DNS > Add New Record (help) | @ for the root, the subdomain name only otherwise |
no Advanced DNS tab means the domain uses other nameservers |
| Vercel | Domains > domain > DNS record form (docs) | prefix only (bounces, not bounces.example.com); blank for the root |
only works with Vercel's nameservers; default TTL is 60 seconds |
| Hostinger (hPanel) | Domains > domain > DNS / Nameservers | @ for the root, the subdomain name otherwise |
hosting plans often create their own SPF and DMARC: edit them instead of adding a second one |
Vercel's own docs point out that switching nameservers doesn't bring your old records along: copy MX, SPF, DKIM and DMARC to the new provider before changing NS, or email stops working.
From symptom to cause#
| What you see | Most likely cause |
|---|---|
everything pending, nothing in dig |
wrong panel (NS points elsewhere) |
| only DKIM and return path pending, on Cloudflare | proxied CNAMEs |
dig answers on the duplicated name |
domain pasted into the name field |
SPF "not verified" or spf=permerror |
two SPF records or more than 10 lookups |
| right at the nameserver, empty on your machine | cache; wait for the TTL |
| value almost matches | whitespace, quotes or a typo |
Where Couryo fits#
In Couryo, every domain record shows its own status: ok, pending (not found yet) or wrong (found with a different value), and for wrong the found field shows what is actually published so you can compare. Pending domains are rechecked automatically every 10 minutes for 72 hours. With Cloudflare connected (a token with permission to edit the zone's DNS), the records are created in your zone and verified right away, no copy and paste. SPF lives on the bounce subdomain, so your root domain's SPF doesn't change and mistake 3 can't happen. See Domains and DNS.
FAQ#
How long does email domain verification take?#
With the right records in the right panel, minutes to a few hours. On Cloudflare and Vercel it's usually minutes. If dig against the domain's nameserver still doesn't show the record after a day, it isn't propagation: check the panel and the name.
The record shows up in dig but the provider says it isn't verified. Why?#
Three usual reasons: the verifier still has a cached "does not exist" (wait for the SOA time), the value has a small difference (space, quotes, a letter), or it's a proxied CNAME on Cloudflare that answers with IPs. Re-run the verification after fixing it.
Can I have two DKIM records on the same domain?#
Yes, as long as they use different selectors (google._domainkey, couryo1._domainkey). Each service uses its own. What you can't have is two SPF records on the same name, or two DMARC records.
Do I have to delete my existing SPF record to add a new email service?#
No. If the new service needs an include on your root domain, merge it into the existing record. If it uses a bounce subdomain (such as bounces.), your root SPF stays as it is.
What does the trailing dot in a DNS name mean?#
It marks the name as complete (example.com.). Without it, some panels append your domain to whatever you typed. If a record was created with the domain duplicated, delete it and create it again with only the short name.