8 MIN READ · Pedro Thomaz

Why transactional email goes to spam — and how to fix deliverability

Transactional email goes to spam mostly because of broken authentication, not bad words. Set up SPF, DKIM and DMARC correctly, send from a verified domain, and align your Return-Path — here's the honest checklist we use with Resend.

Why transactional email goes to spam — and how to fix deliverability

Transactional email goes to spam almost always because of broken or missing authentication — not because the word "free" appears in your receipt. If your password resets, order confirmations and contact-form notifications are landing in the junk folder, the fix is almost never copywriting; it's getting SPF, DKIM and DMARC right, sending from a verified domain, and making sure your Return-Path aligns. This is the practical transactional email deliverability guide we wish we'd had, grounded in the exact Resend-on-PHP setup we run at Amplified Creations.

Why transactional email goes to spam

Receiving mail servers — Gmail, Outlook, Apple iCloud, corporate Microsoft 365 tenants — do not read your email and decide it "looks spammy." They run a set of automated checks first, and the most important ones are about who you say you are versus who you actually are. A receipt that fails authentication is treated as potential spoofing, and spoofing is exactly what spam filters exist to stop. So the receipt for a €40 box of chocolate gets the same suspicion as a phishing attempt, and into the spam folder it goes.

The three records that carry that authentication are SPF, DKIM and DMARC. They live in DNS, they are boring to set up, and they are the single highest-leverage thing you can do for deliverability. Get them wrong and even perfect content lands in spam. Get them right and mediocre content still reaches the inbox. Let's define each one the way an inbox provider actually uses it.

SPF (Sender Policy Framework)

SPF is a DNS TXT record on your domain that lists which servers are allowed to send mail for that domain. When a receiving server gets a message claiming to be from noreply@yourdomain.com, it looks up the SPF record and checks whether the sending IP is on the approved list. A typical record looks like this:

yourdomain.com.  TXT  "v=spf1 include:_spf.resend.com ~all"

The include: mechanism delegates to your email provider's own SPF (here, Resend's). The ~all at the end is a "soft fail" — anything not listed is suspicious but not outright rejected. Two things bite people: you can only have one SPF record per domain (multiple records is a hard error, you must merge the include: entries), and SPF checks the hidden envelope sender (the Return-Path), not the From: header your user sees. More on that below.

DKIM (DomainKeys Identified Mail)

DKIM adds a cryptographic signature to every message. Your provider holds a private key and signs outgoing mail; you publish the matching public key in DNS under a selector. The receiving server fetches that public key, verifies the signature, and confirms the message body and key headers weren't tampered with in transit. The DNS record looks like a long key under a selector subdomain:

resend._domainkey.yourdomain.com.  TXT  "p=MIGfMA0GCSqGSIb3DQ...long-public-key...AQAB"

The resend._domainkey part is the selector — it lets a provider rotate keys and run several in parallel. DKIM is what proves the mail genuinely came from someone holding your domain's key, which is why it's the backbone of the next record.

DMARC (Domain-based Message Authentication, Reporting & Conformance)

DMARC ties SPF and DKIM together and tells receivers what to do when a message fails. It's a TXT record at _dmarc.yourdomain.com with a policy:

_dmarc.yourdomain.com.  TXT  "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com; adkim=s; aspf=s"

The policy p= has three values. p=none means "monitor only, deliver as normal but send me reports" — where you start. p=quarantine means "send failures to spam." p=reject means "bounce failures outright" — the strongest, and where reputable senders end up. The crucial concept DMARC adds is alignment: it's not enough for SPF or DKIM to pass, the domain they pass for must match the domain in the visible From: header. adkim=s and aspf=s request strict alignment. This alignment requirement is precisely why "it sends fine from my script but Gmail flags it" happens — the underlying auth passes for the provider's domain, not yours.

The rua= address is where aggregate reports go. Those reports are gold and we cover them at the end.

SPF DKIM DMARC setup: the parts people miss

Publishing three records is the easy 80%. The 20% that actually decides whether you reach the inbox is below, and it's where we've spent the most debugging hours.

Use a verified sending domain or a dedicated subdomain

Sending "from" a domain you haven't verified with your provider is the fastest route to spam, because none of the authentication can align. A verified sending domain means you've proven control by publishing the provider's DNS records and the provider has confirmed they resolve. We go one step further and send transactional mail from a dedicated subdomain — something like mail.yourdomain.com or send.yourdomain.com — rather than the bare domain. This isolates reputation: if a marketing blast or a misbehaving form ever torches the sending subdomain's reputation, your root domain (and the email your humans send from Gmail) stays clean. Subdomain reputation is largely separate from the parent.

Return-Path and envelope alignment

Every email actually has two "from" addresses. There's the From: header your recipient sees, and there's the hidden envelope sender — the Return-Path, also called MAIL FROM — that SPF actually checks and that bounces go to. When you send through a provider, the Return-Path often defaults to the provider's own domain, which means SPF passes for them, not for you, and DMARC alignment can fail even though "SPF passed." The fix is to let the provider set a Return-Path on your own subdomain (Resend does this via the bounce/feedback records it asks you to publish). When the Return-Path is on your domain and SPF passes there, you get aligned SPF and DMARC is happy.

Reverse DNS, shared IPs, and reputation

Two infrastructure facts shape deliverability and you mostly inherit them from your provider. First, reverse DNS (PTR): a well-behaved mail server's IP resolves back to a hostname, and that hostname resolves forward to the same IP. Mail from an IP with no PTR record is heavily penalised. Second, shared vs dedicated IPs: most senders, us included, sit on a pool of shared IPs managed by the provider. That's good — the pool has warmed-up reputation you couldn't build alone at low volume — but it also means a bad actor on the same pool can dent your delivery. This is the real argument for a reputable provider: they police their pools, handle PTR, warm IPs, and manage feedback loops so you don't have to run a mail server on OVH and beg Microsoft to delist you.

Content and spam-trigger basics

Content matters far less than authentication, but it's not zero. Keep a sane text-to-image ratio (an all-image email reads as suspicious), always include a plain-text alternative alongside the HTML, don't link to shorteners or domains with bad reputation, and don't shout in ALL CAPS with rows of exclamation marks. For genuinely transactional mail — a password reset, an order receipt — this is rarely the problem. It becomes the problem the moment a "transactional" template starts carrying marketing, which leads to the next point.

List-Unsubscribe for anything bulk

The instant your mail is bulk or promotional — newsletters, "we miss you" emails, anything sent to a list — Gmail and Yahoo require a List-Unsubscribe header, and since 2024 a one-click version (List-Unsubscribe-Post: List-Unsubscribe=One-Click). Pure transactional mail is exempt, but the boundary is enforced by behaviour: if you slip a promotion into a receipt and people mark it as spam, your transactional stream pays the reputation price. Keep transactional and marketing on separate subdomains and separate streams. We do.

How we send: Resend on PHP at Amplified Creations

Here's what we actually run, honestly. Amplified Creations is a small independent studio in Leiria and Lisboa; our site is server-rendered PHP 8.3 on OVH shared hosting, behind Cloudflare, with a headless Cockpit CMS and no build step. For email we use Resend with a verified sending domain. Verification in Resend is the same process described above: you add the domain in their dashboard, it hands you a set of DNS records — the DKIM key under a selector, an SPF include:, and the Return-Path/bounce records for envelope alignment — and you publish them at your DNS host. Once they go green, mail is authenticated and aligned.

That single setup powers two things. The first is the site's contact form: a submission triggers a small PHP handler that calls the Resend API to notify us. The second is client transactional mail — the lifecycle messages our applications send. The clearest example is the Stripe suite we built for Delicious Diamonds, a luxury chocolate maker in Leiria. Their store handles one-off orders, bespoke orders, hand-numbered limited editions, and monthly subscriptions with pause, cancel and tier changes — all driven by Stripe lifecycle webhooks. Every one of those events that needs to reach a human — an order confirmation, a subscription paused, a payment that needs attention — goes out through the same authenticated Resend domain. Beyond the storefront our work runs to machine-learning recommenders (Jofit), 3D capture, and accessible and medical VR, but the email principles are identical everywhere: authenticate properly, align the Return-Path, and keep streams separated.

Monitoring with DMARC aggregate reports

Once p=none is live with a rua= address, receivers start emailing you daily XML aggregate reports. They're unreadable raw, so feed them to a DMARC dashboard (there are free tiers). Each report tells you, per sending source, how much mail passed SPF, passed DKIM, and aligned for DMARC. This is how you discover the forgotten WordPress plugin or CRM still sending as your domain and failing alignment — the stuff that would quietly tank your reputation. Only once the reports show your legitimate sources are 100% aligned do you tighten the policy: p=none to p=quarantine, watch a couple of weeks, then p=reject. Tighten before you're clean and you'll start blocking your own receipts.

Short version

FAQ

Why does my transactional email go to spam even though the content is fine?

Because deliverability is decided by authentication before content is even read. If SPF, DKIM or DMARC fail — or pass but don't align with your visible From: domain — the receiving server treats the message as possible spoofing and files it as spam regardless of what it says. Fix authentication and alignment first.

Do I need all three of SPF, DKIM and DMARC?

Yes. SPF and DKIM each prove one aspect of authenticity, and DMARC ties them together with an alignment rule and tells receivers what to do on failure. Gmail and Yahoo now effectively require all three for any meaningful volume, so treat them as a set, not a menu.

What is the difference between the Return-Path and the From header?

The From: header is the address your recipient sees; the Return-Path (envelope sender) is the hidden address SPF checks and bounces return to. They can differ, and that mismatch is why "SPF passed" can still fail DMARC — alignment requires the Return-Path domain to match the From: domain. Configure your provider to set the Return-Path on your own domain.

How does Resend domain verification work?

You add your domain in the Resend dashboard and it gives you DNS records to publish: a DKIM public key under a selector, an SPF include:, and Return-Path/bounce records for alignment. Once those resolve and Resend marks the domain verified, your mail is signed and aligned. We use this exact setup for our contact form and client transactional mail.

Should transactional and marketing email use the same domain?

No — use separate subdomains so reputation is isolated. If a marketing campaign gets spam complaints, you don't want it dragging your password resets and receipts down with it. A dedicated transactional subdomain keeps that critical stream clean and lets you apply a stricter DMARC policy to it.