Mail from your domain lands in spam because the receiving server cannot prove it came from you. Three DNS records fix that: SPF lists the servers allowed to send for your domain, DKIM attaches a cryptographic signature the receiver can verify against a public key in your zone, and DMARC tells the receiver what to do when neither of them lines up with the address your reader actually sees. All three are TXT records. None of them is difficult. What is difficult is the concept in the middle - alignment - and almost every "I set up SPF and it still fails" story is that concept being missed.
One thing to get out of the way first, because it changes what you should be reading: RE:NODE does not host email. No mailboxes, no MX, no SMTP relay, and no DNS zones either - the records below are made wherever your domain's nameservers point, which is usually your registrar. What we host is the application or website that sends the mail, and that part is covered at the end.
Two From addresses, and why that is the whole problem#
An SMTP message has two senders, and they do not have to match.
The envelope sender is what the sending server declares in the MAIL FROM command during the SMTP conversation. It is where bounces go, it is recorded in the Return-Path header, and nobody ever sees it in a mail client. On a form mailer it is frequently something like bounces@sendgrid.net or www-data@server42.hostingco.net.
The header From is the line inside the message that says From: Anna <anna@example.com>. It is the only one a human reads, and it is the one a phisher wants to control.
SPF checks the envelope sender. DKIM signs the message and names a signing domain. Neither of them, on its own, says anything about the address in the From: line - which is the address being abused. DMARC exists to close that gap: it requires that one of the two passing checks refers to the same domain as the visible From:, and it lets you publish what should happen when neither does.
SPF: which servers are allowed to send#
SPF is one TXT record at your domain apex listing every source that may put your domain in the envelope sender.
v=spf1 include:_spf.google.com include:sendgrid.net ip4:203.0.113.10 ~allRead it left to right. The receiver checks each mechanism against the connecting server's IP address and stops at the first match.
| Mechanism | Costs a lookup | What it matches |
|---|---|---|
ip4: / ip6: | No | A literal address or CIDR range |
a | Yes | The A records of the domain |
mx | Yes | The addresses of your MX hosts |
include: | Yes | Another domain's SPF record, evaluated in place |
exists: | Yes | A constructed name that resolves |
all | No | Everything, so it goes last |
The qualifier in front of all is the verdict for everything that did not match: -all is a hard fail, ~all is a soft fail, ?all is neutral, and +all means "anyone may send as me", which is worse than having no record at all.
Four rules decide whether an SPF record works:
- One record per domain. Two
TXTrecords startingv=spf1produce a permanent error and everything fails. If you add a provider, edit the existing record; do not create a second one. - Ten DNS lookups, total.
include,a,mx,exists,ptrandredirecteach cost one, and everyincludespends more inside itself. Past ten the result ispermerror, which most receivers treat as a fail. A record with four mail providers in it is usually already over. Replace what you can withip4:ranges, or use a flattening service if you must. - The `ptr` mechanism is deprecated. Remove it. It is slow, unreliable, and RFC 7208 tells receivers they may skip it.
- SPF does not survive forwarding. When a university alias forwards your mail on, the forwarder's server connects to the final destination with your domain still in the envelope, from an address you never listed. SPF fails and there is nothing you can do about it. This is not a bug in your setup - it is the reason DKIM exists.
Start with ~all while you are still discovering who sends for you, and move to -all only once DMARC reports show you have found everybody.
DKIM: a signature that travels with the message#
The sending server hashes the body and a chosen set of headers, signs the hash with a private key, and adds a DKIM-Signature header naming the signing domain (d=) and the key selector (s=).
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=s1; h=from:to:subject:date:message-id; bh=47DEQpj8HBSa...; b=Xk9pLm2Qv8...The receiver takes d= and s=, looks up s1._domainkey.example.com, and finds the public key:
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...Points that matter in practice:
- You do not generate this yourself in most cases. Your mail provider generates the key pair, keeps the private half, and hands you the
TXTrecord to publish. Google Workspace, Microsoft 365, Postmark, Mailgun and the rest all work this way, and each has its own selector. - Every sender needs its own selector. Transactional mail from your application, marketing mail from a campaign tool and normal mail from your mailboxes are three different signing keys at three different selectors, all in the same zone. They do not conflict.
- A 2048-bit key does not fit in one DNS string. A
TXTrecord is made of chunks of at most 255 characters, so the key has to be split. Nearly every DNS editor does this silently; a few make you split it yourself with quotes. If publishing appears to succeed but verification fails, this is the first thing to check. - DKIM survives forwarding, until something edits the message. A mailing list that appends a footer or prefixes the subject changes bytes that were signed, and the signature breaks. That is what
ARCheaders were invented for, and it is out of your hands as a sender. - Rotate keys occasionally. Publish a new selector, switch the sender to it, wait a week for in-flight mail, then remove the old record.
DMARC: the policy that ties the two together#
DMARC is a single TXT record at _dmarc under your domain.
v=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=r; aspf=r; pct=100| Tag | Default | What it does |
|---|---|---|
p | required | none, quarantine or reject for the domain |
sp | same as p | A different policy for subdomains |
rua | - | Where daily aggregate XML reports are sent |
ruf | - | Where per-message failure reports go, if the receiver sends any |
pct | 100 | Percentage of failing mail the policy is applied to |
adkim | r | DKIM alignment: r relaxed, s strict |
aspf | r | SPF alignment: r relaxed, s strict |
p=none changes nothing about delivery. It only asks receivers to send you reports, which is exactly what you want at the start: a stream of evidence about who is sending as your domain before you tell anybody to throw mail away. p=quarantine means spam folder. p=reject means the receiving server refuses the message during the SMTP conversation, so the sender gets a bounce.
The aggregate reports at rua are gzipped XML, one per receiver per day, and they are not meant to be read by eye. Feed them into any DMARC report parser - there are free ones - and what you get is a table of sending IPs, their SPF and DKIM results, and whether each aligned. That table is the whole point of the exercise.
Alignment is the part that catches people#
This is the concept that makes sense of every confusing result.
DMARC passes when at least one of SPF or DKIM passes and the domain it passed for matches the domain in the visible From: header. Both halves are required. A message can pass SPF cleanly and still fail DMARC, and that combination is the single most common support ticket in this whole subject.
Here is how it happens. Your contact form runs through a transactional provider. The provider sets the envelope sender to bounce@mail.provider.net, because bounces come back to them. SPF is checked against provider.net, and it passes - their record is fine. But the From: header says hello@example.com. provider.net is not example.com, so that pass is not aligned, and it contributes nothing to DMARC. If DKIM is signing with d=provider.net as well, DMARC fails outright and your p=reject throws away your own contact form.
The fix is one of two things, and every serious provider offers both:
- A custom return path. You publish a
CNAMEsuch asbounces.example.compointing into the provider's infrastructure, they use it as the envelope domain, and SPF now passes for a subdomain ofexample.com. Under relaxed alignment - the default - a subdomain aligns with the parent, so DMARC is satisfied. - DKIM signed with your domain. You publish the provider's selector under
example.com, and they sign withd=example.com. This is the more robust of the two, because it also survives forwarding.
Relaxed alignment (r) accepts any subdomain of the organisational domain: mail.example.com aligns with example.com. Strict (s) requires an exact match. Leave both relaxed unless you have a specific reason, because strict alignment breaks custom return paths for no security gain that matters at this level.
Rolling it out without losing mail#
The order matters. Publishing p=reject on day one, on a domain whose senders you have not inventoried, means your invoices stop arriving and nobody tells you.
- List every sender. Mailboxes, the website's contact form, the application's password resets, the invoicing tool, the CRM, the newsletter, the helpdesk, the monitoring alerts, and whatever somebody in accounts set up three years ago. This step takes longer than all the others.
- Get SPF and DKIM right for each of them. One SPF record covering all of them within the ten-lookup budget; one DKIM selector per sender, signing with your domain wherever the provider allows it.
- Publish `p=none` with a `rua` address. Leave it for two to four weeks and read the reports.
- Fix what the reports show. There will be a sender you forgot. There always is.
- Move to `p=quarantine`. Use
pct=25, thenpct=100, if you want a gentler ramp. Watch the reports for another two weeks. - Move to `p=reject`. Keep reading the reports. This is not a thing you finish.
Two extras worth knowing about: the receiving side can be hardened with MTA-STS, which tells senders your domain requires TLS, and a BIMI record - the one that puts a logo beside your mail in some clients - is only honoured on domains already at p=quarantine or p=reject. Neither is a starting point.
Checking it from the outside#
Everything here is public, so you can verify all of it yourself with dig - no tooling required.
$ dig TXT example.com +short$ dig TXT s1._domainkey.example.com +short$ dig TXT _dmarc.example.com +short$ dig MX example.com +shortOn Windows, Resolve-DnsName example.com -Type TXT does the same job. If a record you just published does not appear, it is cached - the answer you get is as old as the TTL allowed. Query your authoritative nameserver directly to bypass that, as described in nameservers vs DNS records, and see DNS records explained for why the cache is always the confusing part.
The other half of the check is to send a message to an account you control and read the headers. In Gmail, Show original prints the verdicts at the top; in most clients, look for the Authentication-Results header:
Authentication-Results: mx.google.com; spf=pass (google.com: domain of bounces.example.com designates 203.0.113.10 as permitted sender) smtp.mailfrom=bounces.example.com; dkim=pass header.i=@example.com header.s=s1; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=example.comThree passes and header.from matching your domain is the finished state. Anything else tells you exactly which of the three to go back to.
What else decides the spam folder#
Authentication gets you considered. It does not get you delivered. Once SPF, DKIM and DMARC pass, the things that still put mail in spam are:
- Reputation of the sending IP and domain. New sending domains start with nothing and are treated cautiously. Send a slow, steady volume for a few weeks rather than ten thousand messages on day one.
- Reverse DNS. The sending IP should have a
PTRrecord that resolves back to the name the server uses inHELO. Shared relays handle this; a server you configured yourself often does not. - Complaint rate. Gmail's published threshold for bulk senders is a spam-complaint rate below 0.3%, with 0.1% as the target. One bad list purchase ruins a domain for months.
- Unsubscribe handling. Since 2024, senders of more than 5,000 messages a day to Gmail or Yahoo must support one-click unsubscribe on promotional mail and honour it within two days, on top of SPF, DKIM and a DMARC record.
- Bounce handling. Keep sending to addresses that hard-bounce and you are telling every receiver you do not maintain your list.
- Content and links. Shortened links, a single large image with no text, and link domains with poor reputation all count against you.
Sending mail from an application you host#
If your Node, Python, PHP or Laravel application needs to send password resets and order confirmations, do not try to make the server itself into a mail server. Outbound port 25 is blocked by most networks, a fresh IP has no reputation, and you would be taking on spam filtering, queueing and bounce processing as a side project.
Use a transactional provider over SMTP submission - port 587 with STARTTLS, or 465 with implicit TLS - or their HTTP API. Configure it with the custom return path and your own DKIM selector from the alignment section above, so DMARC passes. Keep the credentials out of the repository and in the panel's environment variables, as in environment variables and secrets; an SMTP password committed to Git is one of the fastest ways to have your domain used to send spam.
On RE:NODE this is the boundary worth being explicit about: we host the application or site that sends the mail, on app and web plans with a reverse-proxy slot, environment variables on the Startup tab and database slots in the panel. We do not host mailboxes, we do not run MX, we do not relay SMTP, and we do not host DNS zones. Your MX record points at your mail provider, your A record points at us, and the two are independent - which is also the answer to "will moving my website break my email". It will not, as long as you only change the records that concern the website. Nameservers vs DNS records covers which records those are, and if the site itself is a WordPress install, WordPress security hardening covers the plugin that is usually sending the mail.
FAQ#
Do I need all three records?
DMARC needs at least one of SPF or DKIM to pass in an aligned way, so strictly you could run with two. In practice publish all three: SPF fails on forwarding, DKIM fails on mailing lists, and having both means one of them usually survives. DMARC without either is meaningless.
Will a DMARC record stop people spoofing my domain?
It stops mail that claims to be from your domain at receivers who honour DMARC, which now includes every large mailbox provider. It does nothing about lookalike domains, display-name spoofing, or a compromised account sending genuinely authenticated mail. It closes one door, and it is the door most commonly used.
Why does my mail pass SPF but fail DMARC?
Alignment. SPF passed for the envelope domain, which belongs to your sending provider, not for the domain in the visible From: header. Set up a custom return path at a subdomain of your own domain, or have the provider sign DKIM with your domain, and the pass will align.
How long until a change takes effect?
As long as the TTL on the record, plus however long receivers cache it. Lower the TTL to 300 seconds a day before you change anything, and expect to wait the old TTL out after you do. Nothing about mail authentication is instant.
Can I host email on the same plan as my website?
Not here. RE:NODE hosts the website or application; mailboxes, MX and SMTP relaying are somebody else's service. That separation is normal and it is also convenient: you can move the website between hosts without touching a single mail record.
What does p=none actually do?
Nothing to your delivery. It asks receivers to report on what they saw and to take no action on failures. It is the correct first step, and leaving a domain at p=none for years is still better than jumping to p=reject and discovering your senders at the same time as your customers do.




Комментарии
Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.