The developer's guide to receiving inbound email
What it really takes to receive email yourself: MX records, SPF/DKIM/DMARC checks, MIME parsing, dedupe, loop detection, and an honest build-vs-buy call.
Receiving email looks easy. It isn't.
Sending email gets all the attention: deliverability, warm-up, reputation. Receiving it seems like the simple half: point an MX record somewhere, accept a TCP connection on port 25, read some text. Plenty of developers have started a weekend project on exactly that assumption.
The truth is that a production-grade inbound pipeline is a stack of small, unglamorous systems, each of which exists because something on the open internet will abuse you the moment it's missing. This guide walks the whole stack, top to bottom, so you can decide with open eyes whether to build it or buy it.
Layer 1: DNS and the SMTP conversation
It starts with an MX record: the DNS entry that tells the world which host accepts mail for your domain. Publish at least two (a primary and a fallback) because senders treat an unreachable MX as a soft failure and will retry for days, silently, while you wonder where the mail went.
Behind the MX sits an SMTP server speaking a protocol from 1982 with decades of accreted extensions. You'll want STARTTLS (most legitimate senders now expect opportunistic encryption), sensible size limits, and connection-level rate limiting, because within hours of your MX going live, bots will find it and start probing. Port 25 on the public internet is a rough neighbourhood; your logs will prove it by morning.
Layer 2: authentication (SPF, DKIM, DMARC)
The SMTP envelope's 'from' is a free-text field; anyone can claim to be anyone. Three verification mechanisms let you decide how much to trust a message, and skipping them means processing forged mail as if it were real.
SPF answers 'is this server allowed to send for this domain?' You check the connecting IP against the sender domain's published SPF record. DKIM answers 'was this message signed by the domain and unmodified in transit?' You verify a cryptographic signature in the headers against a public key in DNS. DMARC ties them together: the sender's domain publishes a policy saying what to do when both fail, and crucially requires alignment: the authenticated domain must match the From header the human actually sees.
None of these are optional in practice. A pipeline that skips them will happily accept an email 'from' your bank, your CEO, or your own service. And the results should flow into your processing as structured verdicts, not just accept/reject. Downstream logic often wants to treat 'DKIM pass from a known sender' differently from 'no authentication at all'.
Layer 3: parsing MIME, the format that fights back
Once a message is accepted you hold a blob of MIME: a nested multipart structure that real-world senders mangle in every way imaginable. Expect charsets that lie, quoted-printable and base64 encodings stacked on each other, HTML parts with no text alternative, text parts that are actually HTML, and attachments with content types chosen apparently at random.
A robust parser normalises all of it: decode every part to UTF-8, pick or synthesise a plain-text body, sanitise the HTML, extract attachments with real content types, and preserve the headers you'll need later. Use a battle-tested library rather than writing your own, but know that even the good libraries need a defensive wrapper, because a malformed message that crashes your parser is a denial-of-service vector anyone can post to.
Layer 4: dedupe and loop detection
Two failure modes will find you in the first month. The first is duplicates: SMTP is retry-based, so a sender that doesn't receive your acknowledgement in time will send the same message again. Deduplicate on the Message-ID header (every legitimate message carries a globally unique one), with a fallback hash of sender, recipient, date and body for the rare messages without. Store seen IDs with a TTL of a few days; that covers any realistic retry window.
The second is loops, and loops are how inbound systems die at 3am. Your pipeline forwards a message, the destination auto-replies or bounces, the bounce arrives back at your pipeline, which forwards it, which bounces... Defences are layered: honour the Auto-Submitted header and never trigger automation on messages marked auto-generated, send bounces from a null return path so they can't themselves bounce, count Received headers and refuse messages that have passed through too many hops, and rate-limit per sender-recipient pair as a final backstop. Every one of these rules was learned the hard way by someone.
- Dedupe on Message-ID with a content-hash fallback
- Never auto-respond to messages with Auto-Submitted: auto-generated or auto-replied
- Use a null return path (MAIL FROM: <>) for anything your system emits automatically
- Cap the hop count via Received headers; refuse the merry-go-round
- Rate-limit per sender-recipient pair as the last line of defence
Layer 5: the operational tail
The layers above get you a working system. Keeping it working is its own job: IP reputation monitoring so senders don't silently distrust you, storage lifecycle so parsed mail doesn't grow without bound, spam filtering that evolves as attackers do, alerting on delivery anomalies, and TLS certificates that renew themselves. Inbound email is infrastructure in the fullest sense: it's never finished, only maintained.
Budget honestly: the prototype that accepts its first message is a weekend. The system you'd let run unattended while you're on holiday is months, plus a permanent slice of attention forever after.
Build vs buy, honestly
Build it yourself when email infrastructure is your product, when compliance genuinely requires mail never to touch third-party servers, or when you want the deep protocol education. It's a genuinely excellent way to learn how the internet's oldest federated system really works.
Buy (or rather, configure) when email is an input to your product rather than the product itself. A hosted inbound pipeline hands you the whole stack above as configuration: HideMy.world, for instance, gives you addresses that exist on demand, runs the SMTP, authentication, parsing, dedupe and loop detection layers, and delivers clean JSON to a webhook, with filtering rules and AI-based extraction in between if you want them. Your integration effort collapses to writing the HTTP handler that receives the result.
The honest test is a question: do you want to own an SMTP server's pager? If reading layer 4 gave you ideas, build. If it gave you a headache, buy, and spend the reclaimed months on the product only you can write.