Skip to content
FormSubmit

HTML

How to send an HTML form to email (no PHP, no server)

Send an HTML form to email without PHP or a server. Why mailto fails, a copy-paste form that works, subject and reply-to, attachments and spam.

7 min readUpdated Oct 8, 2026

To send an HTML form to email, set the form's action to a hosted form backend endpoint such as https://formsubmit.app/f/YOUR_FORM_ID and its method to POST. HTML can't send email by itself, so the form backend receives each submission, filters spam and emails it to you. Replying answers the visitor directly. You don't need PHP, a mail server or any JavaScript, and it works on any host, including GitHub Pages, Netlify, Vercel and shared hosting.

The rest of this guide explains why that's the case, shows the mailto: shortcut and why it lets you down, and then walks through a working form to email setup, from the subject line to attachments and spam.

Why HTML alone can't send email

A <form> does one job. When someone presses submit, the browser collects the value of every named input and sends it in an HTTP request to the URL in the form's action attribute. That's all. Sending email needs a program that talks to a mail server (SMTP), and a web page in a browser can't do that.

So every "HTML form send email" setup has two halves:

  1. The form, which you write in HTML and which runs in the visitor's browser.
  2. Something that receives the request and sends the email. Traditionally that was a PHP script on your own server. Today it's often a hosted service, called a form backend.

If you want to know exactly what the browser does on submit, read HTML form action explained.

The mailto: approach, and why it fails

The first thing most people try is putting an email address in the action:

mailto-form.html
<!-- Looks simple. Don't ship this. -->
<form action="mailto:you@example.com" method="POST" enctype="text/plain">
  <input type="text" name="name">
  <input type="email" name="email">
  <textarea name="message"></textarea>
  <button type="submit">Send</button>
</form>

This doesn't send anything. It asks the browser to open the visitor's default email app with a draft. In practice:

  • Many visitors have no mail app set up. On a work laptop or a phone that only uses webmail, clicking submit does nothing, or opens an app they've never used.
  • Nothing is sent until they press Send in that app. Many people close it and assume the form worked.
  • The body is messy. Browsers format the fields differently, and some ignore the body altogether.
  • There's no spam filtering, no record and no confirmation page. You can't tell how many messages you lost.
  • Your email address is in the page source, where scrapers will find it.

A mailto: link is fine as a "or email us directly" link next to a form. It isn't a way to submit a form. We compare the two in more detail in mailto form vs form backend.

Send form to email with a form backend

The approach that works is to post the form to a service that sends the email for you. With FormSubmit it takes about two minutes.

Create your endpoint

Sign up with Google or email and click New form. You get a unique endpoint URL:

text
https://formsubmit.app/f/k3m9p2qabx

Notifications go to your account email by default.

Paste the form

Here's a complete, accessible HTML contact form that sends email. Replace YOUR_FORM_ID with your own id:

contact.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">
  <div>
    <label for="fs-name">Name</label>
    <input id="fs-name" type="text" name="name" placeholder="Jane Doe" required>
  </div>
  <div>
    <label for="fs-email">Email</label>
    <input id="fs-email" type="email" name="email" placeholder="jane@example.com" required>
  </div>
  <div>
    <label for="fs-message">Message</label>
    <textarea id="fs-message" name="message" rows="5" placeholder="How can we help?" required></textarea>
  </div>
  <!-- Honeypot: leave this hidden field empty to catch bots -->
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <!-- Optional: where to send people after submitting -->
  <!-- <input type="hidden" name="_redirect" value="https://yoursite.com/thanks"> -->
  <button type="submit">Send</button>
</form>

Check the three essentials

  • action="https://formsubmit.app/f/YOUR_FORM_ID" tells the browser where to send the data.
  • method="POST" is required. Only POST creates submissions.
  • Every field has a name. The browser skips inputs without one, so they never reach your inbox. The name also becomes the label in the email: company_size shows up as "Company size".

Submit a test

Open the page, fill in the form and press Send. You land on a hosted thank-you page, the submission shows up in your dashboard, and an email with every field arrives within seconds.

Want the same form with styling, Tailwind or a different set of fields? Build it in the form generator and copy the code. If you need a ready-made contact form for website projects, start there.

Set the email subject and reply-to

Fields whose names start with an underscore are control fields. They change how the submission is handled and aren't stored as data. Two of them shape the email itself.

Subject line

Add a hidden _subject input to set the subject of the notification email:

html
<input type="hidden" name="_subject" value="New enquiry from the pricing page">

This is useful when the same form appears on several pages. For a default subject that includes what the visitor typed, set a template in the form's notification settings instead, for example New {{service}} enquiry from {{name}}. A _subject field in the submission overrides the template for that one submission.

Reply-To

You don't need to do anything for replies to work. FormSubmit sets the Reply-To header to the visitor's address, so pressing Reply answers them directly. It uses the _replyto field if one is sent, then a field named email, then any field whose name contains "email".

If your form has more than one email field, for example the visitor's address and a colleague's, name the visitor's field email so it's picked first:

html
<label for="email">Your email</label>
<input id="email" type="email" name="email" required>

<label for="colleague">Colleague's email (optional)</label>
<input id="colleague" type="email" name="colleague_email">

You can also send _replyto explicitly, which is handy when you submit with JavaScript and build the data yourself.

Redirect after submit

To send people to your own thank-you page, add _redirect:

html
<input type="hidden" name="_redirect" value="https://yoursite.com/thanks.html">

A redirect URL set in the form's settings always takes priority. See redirects.

Send to multiple recipients (CC your team)

There's no _cc field, on purpose. Recipients are managed in your dashboard under Notifications → Recipients. Each new address gets a confirmation link and only starts receiving submissions after clicking it. This stops anyone from editing your page's HTML and using your form to email addresses that never agreed to it.

If email isn't where your team works, send submissions to Slack, Discord or Google Sheets as well.

Add file attachments

To let visitors attach a CV, screenshot or brief, add enctype="multipart/form-data" and a file input:

upload-form.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST" enctype="multipart/form-data">
  <label for="email">Email</label>
  <input id="email" type="email" name="email" required>

  <label for="resume">CV (PDF or Word)</label>
  <input id="resume" type="file" name="resume" accept=".pdf,.doc,.docx">

  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Send</button>
</form>

Without the enctype, the browser sends only the file name. Files aren't attached to the email directly. They're stored privately, and the email links to them with signed links that expire after 7 days. You can also open them from the dashboard at any time. A plain HTML upload can carry about 4.5 MB per request. For larger files, see file uploads and the file upload form guide.

Block spam

A public form that sends email attracts bots. Spam protection is on for every form and every plan:

  • Honeypot. The hidden _gotcha field in the code above is invisible to people, but naive bots fill it in. Any value means the submission is discarded silently.
  • Content checks. Link-heavy messages, known spam phrases and similar patterns are scored. Anything likely to be spam goes to the form's Spam folder, doesn't email you and doesn't count toward your monthly quota.
  • Rate limits and duplicate detection stop floods from a single source.

For targeted forms, add reCAPTCHA, hCaptcha or Cloudflare Turnstile and restrict submissions to your own site with the domain allowlist. The full playbook is in how to stop form spam.

Optional: send without leaving the page (AJAX)

The plain HTML form redirects to a thank-you page. To show "Thanks!" inline instead, submit with fetch and ask for JSON:

ajax-contact.html
<form id="contact" action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">
  <input type="text" name="name" required>
  <input type="email" name="email" required>
  <textarea name="message" required></textarea>
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Send</button>
  <p class="status" role="status"></p>
</form>

<script>
  const form = document.getElementById("contact");
  form.addEventListener("submit", async (event) => {
    event.preventDefault();
    const status = form.querySelector(".status");
    status.textContent = "Sending…";
    const res = await fetch(form.action, {
      method: "POST",
      body: new FormData(form),
      headers: { Accept: "application/json" },
    });
    const json = await res.json();
    if (json.ok) {
      form.reset();
      status.textContent = json.message;
    } else {
      status.textContent = json.error.message;
    }
  });
</script>

Because action and method stay on the form, it still works as a normal post if the script fails to load. Submit a form with JavaScript covers loading states, errors and React.

Troubleshooting: the email didn't arrive

Work through these in order:

  1. Check the dashboard. If the submission is there, the form works and the problem is delivery. If it isn't, check that the action URL is right and the method is POST.
  2. Check the Spam folder in your dashboard. Test messages like "test test" with links can be scored as spam. Mark them as not spam, or lower the sensitivity.
  3. Check your own spam or junk folder. Notifications come from FormSubmit's own sending address, not from the visitor's address, which helps them get through. Add that address to your contacts or create a filter to be safe.
  4. Check your monthly limit. Over the limit, submissions are still stored, but they're locked and don't send an email until you upgrade or the month resets. You'll get an email warning at 80% and 100%.
  5. Check the digest setting. If delivery is set to a daily or weekly digest, you won't get one email per submission.

For a deeper look at SPF, DKIM and why DIY form emails get filtered, read contact form emails going to spam.

Summary

You can't send an HTML form to email with HTML alone, and mailto: doesn't really send anything. Point the form's action at a form backend, give every field a name, and keep the honeypot. Then use _subject, _replyto and _redirect to shape the result. Moving off an old PHP script? See contact form without PHP.

Frequently asked questions

Can I send an HTML form to email without a server?

Not with HTML alone. A form can only send its data to a URL. Point the action at a hosted form backend such as FormSubmit and it receives the data and emails it to you, so you don't run a server yourself.

Why doesn't action="mailto:" work?

A mailto action only opens the visitor's email app with a pre-filled draft. Many visitors have no desktop mail client set up, the message is never sent unless they press Send, and you get no spam filtering or record of the submission.

How do I change the email subject?

Add a hidden input named _subject, for example "New enquiry from the pricing page". You can also set a default subject template with field variables like {{name}} in the form's notification settings.

Can the form email more than one person?

Yes. Add extra recipients under Notifications, then Recipients, in your FormSubmit dashboard. Each address confirms by clicking a link before it starts receiving submissions.

Can visitors attach files?

Yes. Add enctype="multipart/form-data" and a file input. Files are stored privately and the email links to them with signed links that expire after 7 days.

Is it free?

FormSubmit's free plan includes one form and 50 submissions a month. Spam doesn't count toward the limit.

Keep reading

Your form backend is 60 seconds away

Sign up with Google or email, create a form, paste the endpoint. Free forever for small sites — no credit card.