DEV Community

Cover image for HTML Contact Forms Don't Send Email (Here’s What Actually Does)
Long
Long

Posted on • Originally published at formtorch.com

HTML Contact Forms Don't Send Email (Here’s What Actually Does)

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

A few important details

  • label elements with matching for/id pairs: 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.

  • required attributes: the browser blocks submission if these fields are empty. It's not a substitute for server-side validation, but it catches honest mistakes immediately.

  • autocomplete hints: these tell the browser (and password managers) what kind of data goes in each field, enabling autofill. autocomplete="name" and autocomplete="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:

  1. Create a form
  2. Get an endpoint like this one using Formtorch:
   https://formtorch.com/f/abc123
Enter fullscreen mode Exit fullscreen mode
  1. Use it in your action attribute

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"
/>
Enter fullscreen mode Exit fullscreen mode

After submission, users are redirected there.

Form best practices

A few things that are easy to skip:

  • Use <label> elements, not placeholder as labels.
  • Group related fields with <fieldset> and <legend>.
  • Don't disable the submit button after one click without visual feedback.
  • Put novalidate on 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:

  1. Build a proper HTML form
  2. Point action to a working endpoint
  3. Add a redirect
  4. 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)