HTML forms don’t send email. They never did.
Here’s what’s actually going on and how to build one that works.
If you've ever written a contact form in HTML and expected it to send an email, you've probably run into a frustrating discovery: it doesn't, not on its own.
HTML forms are just a way to package data and send it somewhere. The "sending email" part is always done by a server receiving that data and deciding what to do with it.
That server might be:
- PHP code you wrote
- a Node.js API
- a serverless function
- or a hosted service
But something has to be on the other end.
This guide walks through the full picture:
- how HTML forms actually work
- what your backend options are
- and how to build a form that reliably delivers to your inbox
How HTML forms actually work
When a user submits a form, the browser collects the input values and sends them to the URL in the action attribute, using the HTTP method you specify.
<form action="https://example.com/contact" method="POST">
<input name="email" type="email" />
<button type="submit">Send</button>
</form>
That's it. The browser does a POST request to https://example.com/contact with email=user@example.com in the body. What happens next? Entirely up to whatever is running at that URL.
If nothing is running at that URL, you get a 404. If what's running there doesn't send an email, no email gets sent. The HTML itself has no opinion about email.
The mailto: trap
You might have seen this:
<form action="mailto:you@yoursite.com" method="POST"></form>
This doesn’t actually “send” anything.
It opens the user’s email client with a draft message.
Problems:
- Many users don’t have a desktop email client configured
- On mobile, it’s inconsistent or broken
- You don’t get a copy of submissions
- It feels… ancient
This isn’t a real solution. It’s a workaround that never really worked.
What you actually need
You need something to receive the form submission and handle it.
Here are your options:
| Approach | You maintain | Works on static sites | Setup |
|---|---|---|---|
PHP mail()
|
PHP server | ❌ | painful |
| Custom Node API | server + email service | ❌ | hours |
| Serverless function | function + API key | ✅ | 30–60 min |
| Form backend service | nothing | ✅ | ~2 min |
For most projects, especially static sites, a form backend service is the simplest option.
You send the form data to a hosted endpoint, and it handles storage + email delivery.
Building the form
Here’s a complete, accessible HTML contact form:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Contact</title>
</head>
<body>
<form action="https://formtorch.com/f/YOUR_FORM_ID" method="POST">
<div>
<label for="name">Name</label>
<input
id="name"
name="name"
type="text"
placeholder="Jane Smith"
required
autocomplete="name"
/>
</div>
<div>
<label for="email">Email</label>
<input
id="email"
name="email"
type="email"
placeholder="jane@example.com"
required
autocomplete="email"
/>
</div>
<div>
<label for="subject">Subject</label>
<input
id="subject"
name="subject"
type="text"
placeholder="What's this about?"
/>
</div>
<div>
<label for="message">Message</label>
<textarea
id="message"
name="message"
rows="6"
placeholder="Your message…"
required
></textarea>
</div>
<button type="submit">Send message</button>
</form>
</body>
</html>
A few important details
labelelements with matchingfor/idpairs: screen readers use these to describe inputs. They also make the label clickable, which expands the tap target on mobile. Always write them.type="email"on the email field: the browser validates the format before submitting and shows a numeric keyboard on mobile.requiredattributes: the browser blocks submission if these fields are empty. It's not a substitute for server-side validation, but it catches honest mistakes immediately.autocompletehints: these tell the browser (and password managers) what kind of data goes in each field, enabling autofill.autocomplete="name"andautocomplete="email"are the ones that matter here.
Getting a form endpoint
You need a URL to send submissions to.
You can:
- build your own backend
- use a serverless function
- or use a hosted form backend
For example, with Formtorch:
- Create a form
- Get an endpoint like this one using Formtorch:
https://formtorch.com/f/abc123
- Use it in your
actionattribute
Redirecting after submission
You’ll usually want to send users to a confirmation page.
<input
type="hidden"
name="_redirect"
value="https://yoursite.com/contact/thank-you"
/>
After submission, users are redirected there.
Form best practices
A few things that are easy to skip:
- Use
<label>elements, notplaceholderas labels. - Group related fields with
<fieldset>and<legend>. - Don't disable the submit button after one click without visual feedback.
- Put
novalidateon the form if you're handling validation yourself in JavaScript.
What about spam?
Every publicly accessible contact form gets hit by spam. How quickly depends on how prominent your site is, but it happens to everyone eventually.
Options:
- CAPTCHA (bad UX, use PoW CAPTCHA)
- rate limiting
- honeypots
- spam detection services
If you’re building your own backend, you’ll need to handle this yourself.
Testing your form
Before shipping, test the full path:
- Fill in all required fields and submit. Do you land on your thank-you page?
- Check your Formtorch dashboard. Does the submission appear?
- Check your email. Did the notification arrive?
- Try submitting with required fields empty. Does the browser block it?
- Try submitting with an invalid email. Does the browser catch it?
The full picture
HTML forms are just a delivery mechanism.
The real work happens at the endpoint.
The simplest path:
- Build a proper HTML form
- Point
actionto a working endpoint - Add a redirect
- Set up email notifications
That’s it.
If you want to skip building and maintaining a backend entirely, you can use a form backend service.
For example, you can plug your form into something like Formtorch and get:
- an endpoint URL instantly
- email notifications out of the box
- spam filtering handled for you
No server, no API keys, no infrastructure to maintain.
If you're curious, you can check it out here:
👉 https://formtorch.com
Either way, once you understand how the request → endpoint → email flow works, forms stop feeling like magic (or frustration) and start feeling predictable.
Top comments (0)