# Send an HTML form to email without PHP

> Replace a contact.php script that calls mail() with a form that posts to a hosted endpoint, keeping your field names, your thank-you page and replies.

Published 2026-10-25 · https://ikiform.com/guides/send-form-to-email/without-php

Change the form's `action` from your PHP script to a form backend's URL, keep the field names, and delete the script. The backend does what `mail()` did, plus the parts it never did well: spam filtering, storage and mail that doesn't land in spam. It works on static hosts where PHP can't run at all.

## What's different here

Most PHP contact forms are the same twenty lines, copied from a tutorial years ago. They read `$_POST`, build a string, call `mail()`, and redirect. Here's a typical one:

`contact.php`:

```php
<?php
$name = $_POST["name"];
$email = $_POST["email"];
$phone = $_POST["phone"];
$message = $_POST["message"];

$to = "hello@example.com";
$subject = "Website enquiry from $name";
$body = "Name: $name\nEmail: $email\nPhone: $phone\n\n$message";
$headers = "From: $email\r\nReply-To: $email";

if (mail($to, $subject, $body, $headers)) {
  header("Location: /thanks.html");
} else {
  echo "Sorry, something went wrong.";
}
```

It looks harmless, and it worked when it was written. Today it breaks in four ways, and only the first is obvious:

| Problem | Why it happens |
| --- | --- |
| No PHP on the host | GitHub Pages, Netlify, Vercel, Cloudflare Pages and S3 serve files; they don't run PHP. The form posts to a file that is never executed, and you get a `404` or `405`. |
| Mail lands in spam, or vanishes | `mail()` hands the message to the server's local mail program. The `From:` header claims the visitor's address, but the mail leaves your host's server, so it fails the visitor domain's SPF and DMARC checks. Gmail and Outlook filter or reject it, often silently. |
| Header injection | `$email` goes into the headers unchecked. A bot that posts a value with a line break can add its own `Bcc:` line and use your form to send spam, which gets your host's IP blocklisted. |
| Nothing is kept | If the email is lost, so is the message. There's no record, no spam filter and no way to look back at last month's enquiries. |

You could fix each one: validate every value, send through an SMTP provider with a library such as PHPMailer, set up SPF and DKIM for your domain, add a honeypot and rate limit, and log to a database. That's a reasonable project if you already run PHP. If you'd rather not, move the work to a form backend.

Diagram: The same form with PHP mail() and with a form backend

1. With contact.php, the form posts to your host, and mail() sends a message that claims to come from the visitor. Receiving servers see a sender that doesn't match the domain and file it as spam or drop it.
2. With a form backend, the form posts the same fields to Ikiform. Ikiform sends the email from its own domain and puts the visitor's address in Reply-To, which is what Reply-To is for.

The fix for deliverability isn't a better script. It's sending from a domain that is allowed to send.

## Move the form off PHP

Keep the field names exactly as they are. Only the `action` changes, plus two hidden inputs:

`Before`:

```html
<form action="/contact.php" method="post">
  <input name="name" required />
  <input name="email" type="email" required />
  <input name="phone" type="tel" />
  <textarea name="message" required></textarea>
  <button>Send</button>
</form>
```

`After`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <input name="name" required />
  <input name="email" type="email" required />
  <input name="phone" type="tel" />
  <textarea name="message" required></textarea>
  <input type="hidden" name="_redirect" value="https://example.com/thanks.html" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send</button>
</form>
```

Then map each thing the script did to where it lives now:

| In contact.php | Now |
| --- | --- |
| `$to = "hello@example.com"` | Recipients under the form's **Notifications** settings |
| `header("Location: /thanks.html")` | The `_redirect` hidden input, or **Redirect after submit** in the form's settings |
| `Reply-To: $email` | Automatic, because the field is named `email` |
| `$subject = "Website enquiry from $name"` | Fixed as `New submission: <form name>`. Name the form "Website enquiry" to keep the wording |
| `echo "Sorry, something went wrong."` | Ikiform's error page for a plain post, or your own message with `fetch` |
| Checks you never wrote | The `_gotcha` honeypot, spam scoring, rate limits and field validation |

- Add your domain under **Allowed domains**. `_redirect` only works for an `https:` URL on one of those domains.
- Delete `contact.php` from the server once the new form works. A forgotten script is still an open mail relay for bots that remember the URL.
- If you moved to a static host, the old `/contact.php` URL no longer exists. Bots that post straight to it get a `404`, which is fine.

> If your form was a WordPress plugin rather than a script, the same idea applies. The [WordPress guide](https://ikiform.com/docs/guides/wordpress.md) shows where to put the form in a block theme.

## Check it works

1. Under the form's **Allowed domains**, add your site's domain, plus `localhost` while you develop. Leave it empty to accept posts from anywhere.
2. Submit the form on your live page. You land on your old `thanks.html`.
3. Open **Submissions** in the dashboard. The new submission is there with every field you sent.
4. Open the email. It comes from Ikiform, not from the visitor's address, and isn't in spam. Add the sender to your contacts so later ones aren't either.
5. Press **Reply**. The visitor's address is in the **To** line.
6. Post with the honeypot filled in, `_gotcha=bot`. It still gets a success answer, but the submission is stored as spam and sends no email or webhook.

## Gotchas

- **Old form names with brackets.** PHP forms often use names like `contact[email]`. Ikiform stores them as they are, but the reply-to only comes from a field named `email` or a few close variants. Rename the field, or add `_replyto`.
- **The subject can't include the name.** The script's subject line had the visitor's name in it. The notification subject is fixed, and `_subject` is passed to webhooks and the API without changing the email.
- **Character encoding.** `mail()` scripts often mangled accents because they never set a charset. Ikiform's email is UTF-8, so names in any script arrive intact.
- **Multiple addresses in `$to`.** A comma list in the script becomes separate recipients in the dashboard. Outside addresses confirm by link before they get anything. See [multiple recipients](https://ikiform.com/guides/send-form-to-email/multiple-recipients.md).

## If something goes wrong

| Problem | What to do |
| --- | --- |
| The browser lands on Ikiform's thank-you page instead of yours | `_redirect` is ignored unless its domain is in **Allowed domains** and it is an `https:` URL. Add the domain, or set **Redirect after submit** in the form's settings instead. |
| `403 origin_not_allowed`, or every post lands in spam | The page's domain isn't in the form's **Allowed domains**. Add it, plus `localhost` for local tests. Subdomains of a listed domain are allowed. |
| The form still posts to the PHP file | A cached page or a second copy of the form, such as a footer include, still has the old `action`. Search the site for `contact.php`. |
| The submission is in the dashboard but no email or webhook arrived | It was stored as spam. Spam sends nothing. Open it under the **Spam** filter to see the reasons, and mark it as not spam to send its email and webhooks. |

## Questions

### Will my old form keep working while I switch?

Yes. The old script keeps working until you change the form's action, so you can test the new endpoint on a copy of the page first, then switch and delete the script.

### Can I send an HTML form to email without PHP?

Yes. Point the form's action at a form backend instead of a PHP script. The backend receives the post and emails you, so your site can be plain HTML on any static host.

### Why do emails from PHP mail() go to spam?

Scripts usually set the From header to the visitor's address, but the mail leaves your host's server. That fails the visitor domain's SPF and DMARC checks, so Gmail and Outlook treat it as forged. A form backend sends from its own domain and puts the visitor in Reply-To instead.

### Do I need to change my field names?

No. Ikiform stores whatever names you post. Keep the email field named email, email_address, your_email, reply_to, contact_email, or add a _replyto field, so replies go to the visitor.

### Is PHPMailer with SMTP a better option?

It fixes deliverability if you send through a provider with your domain set up for SPF and DKIM. You still need a PHP host, validation, spam filtering and somewhere to keep the messages. It's a good choice if you already maintain PHP code.

## More guides

- [Send a form to your email](https://ikiform.com/guides/send-form-to-email.md): Get every form submission in your inbox without a mail server: why mailto: and PHP mail() fail, and a setup that filters spam and lets you reply.
- [To Gmail](https://ikiform.com/guides/send-form-to-email/gmail.md): Get form submissions in Gmail without mailto: links, app passwords or Apps Script, then label them, keep them out of spam and reply from Gmail.
- [Reply from your inbox](https://ikiform.com/guides/send-form-to-email/reply-to.md): Make Reply on a form notification write to the person who filled in the form: which field names work, when to use _replyto, and why replies go astray.
- [Multiple recipients](https://ikiform.com/guides/send-form-to-email/multiple-recipients.md): Email one form to your whole team, or route each message to the right person by topic, without letting the form itself pick who gets the mail.
- [EmailJS alternative](https://ikiform.com/guides/send-form-to-email/emailjs-alternative.md): Move a contact form off EmailJS: why browser-side email keys are a risk, what changes in your code, and what you give up, such as the auto-reply.
- [Next.js](https://ikiform.com/guides/send-form-to-email/nextjs.md): Two ways to email a Next.js form with a Server Action: send it yourself with Resend or Nodemailer, or forward it to a form backend, compared honestly.
