Contact Form Without PHP
Your site is static, or your host no longer runs PHP, so the old contact.php does nothing. The replacement is the same HTML form, pointed at an address that delivers each submission to your email.
<form action="https://catchform.dev/f/YOUR-TOKEN" method="POST">
<input type="hidden" name="_gotcha" value="">
<input name="name" type="text" required>
<input name="email" type="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
Replace YOUR-TOKEN with the token of your form.
If you already have a form, only its action changes; keep your fields as they are.
What a Typical contact.php Actually Did
Most tutorials on contact forms in PHP ship the same short script. It is worth reading once, because it shows exactly what any replacement has to do.
<?php
$to = 'you@example.com';
$subject = 'New contact form message';
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$message = $_POST['message'] ?? '';
$body = "Name: $name\nEmail: $email\n\n$message";
$headers = "From: noreply@yourdomain.com\r\nReply-To: $email";
mail($to, $subject, $body, $headers);
header('Location: thanks.html');
exit;
The script did three things. It accepted the POST request, it built the message from the fields the visitor
filled in, and it answered the browser with a redirect. It never checked whether mail()
had really accepted the message, and it had no spam protection at all. Any replacement has to cover the same three steps
plus the two gaps.
Why It Stops Working on Static Hosting
The form still looks the same and the visitor still clicks Send. What changes is what the server does with the request.
On a static host nothing runs PHP. The server either sends the .php file back as plain text, so the
visitor sees your source code, or it answers with an error such as 404 or 405. In both cases mail() is never called and
nothing reaches your inbox.
Hosts that serve static files without running PHP include Netlify, Vercel, Cloudflare Pages, GitHub Pages and plain object storage buckets. Uploading a PHP file to any of them changes nothing about how the form works.
The failure is quiet. The visitor gets a blank page, a page full of code or an error page, and you get no notification, because no part of the chain knows that a message was lost. You usually find out only when someone tells you they never got a reply. If your whole site is static, the wider picture is in our guide to a Static site contact form.
Side by Side: Old Form and New Form
The two forms below differ in one attribute: action. Field names, labels and the button are identical,
so nothing else on the page has to change. The honeypot field is left out here on purpose, so the difference really is one attribute;
it is covered in the section on existing forms below.
<form action="contact.php" method="POST">
<input name="name" type="text" required>
<input name="email" type="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
<form action="https://catchform.dev/f/YOUR-TOKEN" method="POST">
<input name="name" type="text" required>
<input name="email" type="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
What to Do With an Existing Form
You do not need to rewrite the form. Keep every name attribute exactly as it is. The names become
the labels of the fields in the email and in your CatchForm dashboard, so a field called phone arrives as phone,
not as something new.
Two changes are needed. First, point the action at your CatchForm address. Second, add the hidden honeypot input. Its name is taken from the application configuration, so it always matches what the server checks:
<input type="hidden" name="_gotcha" value="">
Keep the value empty. Real visitors never see the field. Bots that fill in every input they find do fill it, and their submissions are rejected as spam.
The email field also matters for replies. A field named email (or Email, or e-mail)
with a valid address as its value becomes the Reply-To header, so pressing Reply in your mail client writes straight to the visitor.
The full flow is described in the guide on HTML form to email.
What Happens After the Visitor Clicks Send
What the visitor sees depends on how the request was made and on whether a redirect is set:
- No redirect, regular submission from a browser. The browser is sent to the thank-you page that CatchForm hosts on catchform.dev. That page shows neither your form's identifiers nor the sender's data.
-
No redirect, programmatic request. A request made with
fetch(),curl, or withAccept: application/json, gets a 200 response with{"success": true}. This is useful when your JavaScript sends the form without reloading the page. - Redirect configured. The visitor is sent to the URL set under Redirect after submit in the form settings. This applies to every client, whether the request comes from a browser or from code.
If you want visitors to land on your own thank-you page, set that URL in the form settings. Your old script did the same
job with header('Location: thanks.html'); the setting does it without any server code.
Why mail() Often Lands in Spam
mail() hands the message to the mail server of the host. On shared hosting that server often has no SPF or DKIM records
for the domain in your From address, or the records do not match the sender. Receivers such as Gmail and Outlook check those records.
When the checks fail or do not line up, the message is often rejected or moved to spam.
The difference is that mail() returns true as soon as the message is accepted for local delivery. The script
cannot tell whether the message later went to spam. A mail service sends through infrastructure that is set up to authenticate
its messages, instead of relying on whatever the shared host has configured. That still does not guarantee that every message reaches the inbox;
no sender can promise that.
The part that matters most for a contact form is the copy. CatchForm stores every submission that passes its checks in your dashboard. If a message lands in spam, the record is still there to read, so a lead does not disappear.
When PHP Is Still the Right Answer
Sometimes a mail() script is the right choice, and this guide does not argue otherwise. If your host already runs PHP, you control its mail
setup, and you only get a handful of messages a week, a contact.php is a legitimate answer. Keep it.
The cost moves to you. With your own script you are responsible for:
- spam filtering, such as a honeypot field, a captcha or rate limits;
- deliverability: SPF, DKIM and, ideally, DMARC for the sending domain, and a proper SMTP setup if
mail()is not reliable enough; - storage: if you want to keep submissions, you write them to a file or a database and build a way to read them.
A small script on PHP hosting is reasonable when one person reads the messages, they go to one inbox, and you are willing to maintain the mail setup. It stops being reasonable when you need a stored record you can search, an export, notifications to several people, or a site with no PHP at all.
If you are choosing a ready-made service instead of writing your own script, we compare the options side by side: CatchForm vs Formspree and CatchForm vs FormSubmit.
Get Started
Ready to replace contact.php? Change the action in your form, add the honeypot line, and Sign up for free to get your first 100 submissions included.