Rebuilding Email for the Modern Web: Why I Killed SMTP
- 08 Aug, 2025
Rebuilding Email for the Modern Web: Why I Killed SMTP
Email is a dinosaur.
Let’s just get that out of the way. It’s a magnificent, resilient, and shockingly useful dinosaur that somehow survived the meteor. But it’s still a dinosaur.
The protocols that make it work—SMTP, POP3, IMAP—were designed by geniuses in an era of dial-up modems and text-only terminals. They were built for a different internet. A smaller, more trusting, and infinitely simpler internet.
For months, I’ve been haunted by a question.
What if we built email today? From scratch. Using the tools we have now.
This question took over. It stopped being a hypothetical and became an obsession. I tore down the old structure in my mind and started building.
The result is a complete, full-stack, end-to-end mail system. A slick React/Next.js frontend. A powerful Python FastAPI backend.
And here’s the kicker. There is no SMTP server. No IMAP daemon. No POP3 listener.
The entire system runs on one thing. The same thing that powers nearly every modern web application on the planet. HTTPS.
This is the story of that project. It’s a manifesto for why the old way is broken. And why the solution has been staring us in the face all along.
The Ghosts in the Machine
To build something new, you have to respect the old. Even if you plan on tearing it down.
Traditional email is a three-headed beast.
SMTP (Simple Mail Transfer Protocol)
This is the postman.
Its only job is to move your email from your server to someone else’s server.
It’s a text-based conversation.
HELO
MAIL FROM:
RCPT TO:
DATA
QUIT
Simple.
But it’s also, by default, like sending a postcard.
Anyone on the path can read it.
Sure, we bolted on security with STARTTLS, but it’s still a patch on a fundamentally insecure design.
It’s a relic.
POP3 (Post Office Protocol) This is your grandma’s mailbox. You check it once a day. You take all the letters out. You take them home. The mailbox is now empty. If you want to read those letters at your office, too bad. They’re at home.
In a world where I live on my laptop, my phone, and my tablet, POP3 is actively hostile to my sanity. It’s a one-way street. It’s broken.
IMAP (Internet Message Access Protocol) This was the fix. The clever one. IMAP syncs your mailbox. Read an email on your phone, and it’s marked as read on your laptop. Your mail lives on the server, and you access it from anywhere.
It works. It’s what most of us use. But it’s also insanely complex. It’s a stateful, chatty, high-maintenance protocol that needs its own dedicated server process, its own port, its own universe of configuration hell.
Here’s the core problem.
It’s three different things. Three different servers. Three different ports. Three different headaches.
It’s a tangled, messy, and unnecessarily complicated system.

The Lightbulb Moment: A Lesson from the Fediverse
Before I show you the new way, let’s talk about Mastodon.
Mastodon is a decentralized social network.
Thousands of independent servers, all talking to each other.
A user on mastodon.social can follow a user on tech.hub.
Seamlessly.
How? Does it use some secret, complex “Social Media Transfer Protocol”? No. It uses ActivityPub.
And what’s ActivityPub? It’s a fancy name for sending structured JSON objects over HTTPS. That’s it. That’s the whole trick.
When you post a “toot,” your server sends a POST request to your followers’ servers.
The body of that request is a JSON object describing your post.
This was the revelation. The Fediverse solved decentralized communication without reinventing the wheel. They just used the web’s native language. They used the tool that was already perfect for the job.
If a global, decentralized social network can run on simple HTTPS requests… Why on earth can’t email?
The Blueprint: My HTTPS-First Mail System
So I built it. I took the core idea from ActivityPub and applied it to email. The entire architecture rests on two pillars. JSON for the data. HTTPS for the transport.
Here’s how an email lives and breathes in my system.
The Data: A Clean, Strict EmailModel
First, no more parsing arcane MIME headers. An email is a clean, predictable JSON object. Defined in my FastAPI backend with Pydantic, it looks something like this:
{
"from": { "id": "sujal", "host": "my-server.com" },
"to": [{ "id": "bob", "host": "another-server.com" }],
"subject": "This is the future",
"body": "The email body, as a simple string.",
"attachments": [
{
"hash": "a1b2c3d4...",
"filename": "project_plan.pdf",
"mimeType": "application/pdf"
}
],
"date": "2025-03-15T10:00:00Z"
}
This is our universal language. The frontend sends it. The backend understands it. It’s simple, extensible, and sane.
The Send-Off: Killing SMTP with a POST Request
This is the heart of it all.
When you click “Send” in my React app…
No connection to port 25.
No HELO greeting.
No text-based dance.
Just one, single, beautiful API call.
POST /email/send
The body of that request is the EmailModel JSON object.
Authentication is handled with a standard Authorization: Bearer <TOKEN> header.
It’s clean.
It’s secure.
It’s how the modern web works.

The backend send_email function then acts as the new postman.
For each recipient, it looks at their host.
And it makes its own HTTPS request.
# A look inside my email_service.py
def send_email_to_server(email_data):
recipient_host = email_data["to"][0]["host"]
url = f"https://{recipient_host}/email/receive"
# This is the "delivery."
# A standard, secure, web-native POST request.
requests.post(url, json=email_data)
The server-to-server transfer is no longer a cryptic chat. It’s a modern API call.
The Arrival: A Smarter Mailbox
The recipient’s server is listening.
Not on some weird port.
It’s just listening for web traffic.
It has one endpoint: /email/receive
When the POST request from the sender’s server arrives, it does a few simple things.
- Validates the JSON against its own
EmailModel. - Checks if the recipient is a local user.
- Adds its own metadata, like a
deliveredAttimestamp. - Saves the entire JSON object to the user’s inbox folder.
_data/inbox/bob/a4b5c6d7.json
Done. The email is delivered. The entire journey was secure, predictable, and handled with the same tools I’d use to build any other web service.
The Superpowers of a Modern Architecture
This isn’t just about replacing old tech with new tech. This is about unlocking capabilities that were nightmares to implement before.
True End-to-End Encryption Becomes Trivial
The body of my email is just a string.
What if, before sending, the client encrypts it?
The JSON payload would look like this:
{
"body": null,
"encryptedBody": "U2FsdGVkX1+aBc...",
"encryptionAlgorithm": "AES-256-GCM"
}
The server is now completely blind. It knows who sent the email and who it’s for. But the content is meaningless gibberish. It stores the gibberish. It delivers the gibberish.
Only the recipient’s client, with the correct private key, can unlock the message.
True, zero-knowledge, end-to-end encryption is no longer a hack. It’s a natural feature of the architecture.

One Channel to Rule Them All
Right now, my system sends EmailModel objects.
But what if we sent something else?
A chat message:
{
"type": "chat_message",
"from": { "id": "sujal", "host": "my-server.com" },
"text": "Hey, you free?"
}
A calendar invite:
{
"type": "calendar_invite",
"organizer": { "id": "sujal", "host": "my-server.com" },
"title": "Project Sync",
"startTime": "2025-03-18T14:00:00Z"
}
The exact same channel—the same /send and /receive endpoints—can handle it all.
Email.
Chat.
Calendars.
File transfers.
We eliminate the need for XMPP, CalDAV, and a dozen other single-purpose protocols. We get one unified, extensible, decentralized communication protocol for the modern web. The simplification is staggering.
Why a POST Request Is Just Better
Let’s put it head-to-head.
My POST /send vs. the old SMTP conversation.
Authentication:
- Me: A standard
Authorization: Bearerheader with a JWT. Secure, stateless, and a solved problem. - SMTP:
AUTH PLAIN. A bolted-on extension that sends credentials back and forth. Clunky.
Data:
- Me: Rich, validated JSON. Structured from the start.
- SMTP: Plain text that needs complex, buggy MIME encoding for anything beyond ASCII.
Error Handling:
- Me:
404 Not Foundif the user doesn’t exist.403 Forbiddenif you’re not allowed. A meaningful JSON error body. - SMTP:
550 No such user here. A cryptic 3-digit code from the 1980s.
Infrastructure:
- Me: HTTPS on port 443. The lifeblood of the web. It’s always open.
- SMTP: Port 25. Blocked by most ISPs to fight spam, forcing workarounds and configuration nightmares.
It’s not even a fair fight.
A Call for a New Beginning
I started this project out of frustration. Out of a belief that we could do better. It has now become a conviction.
The protocols of the past served us well. But their time is over. They are a liability. A source of complexity, insecurity, and limitations.
We have the tools. We have the patterns. We have the web.
My project is proof. A working blueprint for a future where email is just another modern web service. A future where our most essential communication tool is simple, secure, and built for the world we live in today.
It’s time to let the dinosaurs rest. It’s time to build what comes next.
