How to Send Email From Your App Without Landing in Spam
Password resets, receipts, and sign-up confirmations that land in spam — or never arrive — are one of the most common launch-week problems. What SPF, DKIM, and DMARC actually do, how to set them up for your domain, and why your app should never send mail itself.
Your app needs to send email: sign-up confirmations, password resets, receipts, notifications. In development, you tested it and it worked. In production, users report that the reset email never came — or it's sitting in their spam folder next to offers for miracle supplements.
Email deliverability is its own small field, but for an app's transactional email, getting it right comes down to a handful of steps. This guide covers them in plain terms.
Transactional vs marketing email
Two kinds of email, handled differently:
- Transactional — triggered by something a user did: "reset your password," "your order shipped," "someone replied to your comment." Expected, wanted, and time-sensitive.
- Marketing — sent to a list: newsletters, announcements, promotions.
This guide is about transactional email. Keep marketing email separate — ideally a different service, or at least a different sending subdomain — so a newsletter that annoys people can't damage the reputation your password resets depend on.
Rule 1: don't send from your own server
It's tempting to have the app send mail directly, or through your personal Gmail account. Don't:
- Server IP addresses have no sending reputation, and many cloud IP ranges are treated as suspicious by default. Some hosts block outgoing mail ports entirely.
- Personal mailbox accounts have sending limits and aren't meant for automated mail. You'll hit limits, and you're putting your personal account at risk.
Use a transactional email provider: Postmark, Resend, SendGrid, Amazon SES, Mailgun, and others. They maintain sending reputation, handle bounces, give you logs of every message, and provide a simple API. Most have a free or cheap tier that covers a small app comfortably.
The same goes for authentication emails. Auth services like Supabase include a built-in email sender for testing, but it's heavily rate-limited — connect your own provider before launch.
Rule 2: send from your own domain
Send from [email protected], not from a generic address. To do that, you have to prove to receiving mail servers that your email provider is allowed to send on your domain's behalf. That's what three DNS records do.
If DNS is new to you, How to Connect a Custom Domain to Your App explains records first.
SPF: who may send
An SPF record lists the services allowed to send email for your domain. It's a TXT record on your domain that looks something like:
v=spf1 include:_spf.your-email-provider.com ~all
Your provider tells you exactly what to include. One important detail: a domain can only have one SPF record. If you already use Google Workspace or Microsoft 365 for your own mailbox, add the provider's include: to the existing record rather than creating a second one. Two SPF records breaks both.
DKIM: proof the message wasn't changed
DKIM adds a cryptographic signature to each email. Receiving servers check it against a public key published in your DNS. Your provider gives you the record — usually one or more CNAME or TXT records with names like something._domainkey.yourapp.com. Copy them exactly.
DMARC: what to do when checks fail
DMARC ties the other two together. It tells receiving servers what to do with mail that claims to be from your domain but fails SPF and DKIM, and where to send reports. It's a TXT record on _dmarc.yourapp.com. A cautious starting point:
v=DMARC1; p=none; rua=mailto:[email protected]
p=none means "don't reject anything yet, just report." Once reports show that your legitimate mail passes, tighten it to p=quarantine and eventually p=reject — which stops other people sending email that pretends to be from you.
Gmail and Yahoo now require bulk senders to have SPF, DKIM, and DMARC in place. Even for a small app, setting up all three is the difference between the inbox and spam.
Rule 3: use a sending subdomain
Many providers recommend sending from a subdomain — mail.yourapp.com or notify.yourapp.com — rather than the root domain. It keeps your app's sending reputation separate from your own mailbox and from any marketing email. Your provider's setup guide will say which records go on which name.
Rule 4: write emails that look like real email
Content matters less than authentication, but it still matters:
- A plain, recognisable "From" name — your app's name, not "noreply."
- A subject that says what it is — "Reset your YourApp password," not "Action required!!!"
- A text version alongside HTML. Most providers generate one automatically.
- Links to your own domain, not a URL shortener.
- For anything non-essential, an unsubscribe link. Required for bulk mail and good manners everywhere.
Rule 5: handle bounces and complaints
When an email bounces (address doesn't exist) or someone marks it as spam, your provider records it. Keep sending to those addresses and your reputation drops. Most providers automatically suppress addresses that hard-bounce or complain; make sure that's switched on, and don't disable it.
Testing it
- Send to real inboxes at several providers: Gmail, Outlook, iCloud, and a work address if you can. Check the inbox and the spam folder.
- Look at the headers. In Gmail, "Show original" displays whether SPF, DKIM, and DMARC passed. You want
PASSon all three. - Use your provider's logs. Every message's delivery status is recorded. When a user says "I never got it," the log tells you whether it was delivered, bounced, or never sent.
- Test the full flows — sign-up, password reset, receipt — in production, after your domain is live. Links inside emails are built from your app's URL; a setting left on
localhostproduces emails full of broken links.
The checklist
- Transactional email provider connected; app never sends mail directly
- Auth service configured to use your provider, not its built-in sender
- Sending from your own domain (or a sending subdomain)
- SPF record — only one per domain, with the provider included
- DKIM records added exactly as given
- DMARC record, starting at
p=none, reports monitored - SPF, DKIM, and DMARC show
PASSin a real inbox - Bounce and complaint suppression enabled
- Every email flow tested in production
Do this once, properly, and email stops being something you think about.
EasySpawn connects your own domain to your app with automatic SSL and keeps your email provider's API keys in server-side environment variables, where they belong. See how it works for AI-built apps or join the waitlist.
Related: How to Add Login to an AI-Built App · Why Does My App Work Locally but Not in Production? · Does My App Need a Privacy Policy? · Custom Email for Your Domain · DNS Records Explained
Keep reading
How to Connect a Custom Domain to Your App: DNS Without the Jargon
Your app works at a long platform URL, and you want it at yourname.com. What DNS records actually do, A vs CNAME in plain English, the apex-domain problem, how HTTPS gets issued, and how to fix the usual errors.
Why Does My App Work Locally but Not in Production?
The app runs perfectly on your machine and breaks the moment it's deployed. It's almost always one of about a dozen causes — missing environment variables, localhost URLs, a filesystem that doesn't persist. How to find which one, in the order most likely to be it.