August 9, 2026
How to add a contact form to a static site (no backend required)
Static sites are great at serving pages and terrible at receiving forms. A form is a POST request to a server, and a static site — by design — has no server. So when a visitor clicks submit on your contact page, the browser tries to POST the data somewhere, and there is nowhere for it to go. This guide shows you how to fix that without running a backend, writing an API, or adding a database.
Your options, briefly
- A mailto: form opens the visitor's email client and rarely delivers what you expect. Skip it.
- A third-party widget embeds someone else's markup and redirects your visitors through their pages.
- A form backend service (like FormHook) is a URL your form posts to. Submissions land in a dashboard, spam is filtered, and you can export them as CSV.
The rest of this article assumes you want the third option: a plain HTML form that posts to a form backend. It is the least code, keeps your site static, and works on any host — GitHub Pages, Netlify, Cloudflare Pages, or a folder uploaded over FTP.
What a form backend does
A form backend is exactly what the name says: the backend for your form. You give it a URL, it accepts the standard form-data POST that a browser sends, stores the submission, and makes it visible in a dashboard. You write zero server code. From the browser's point of view, your form posts to an ordinary URL — the fact that the site itself is static is invisible to the visitor.
Three steps to a working contact form
First, get an endpoint URL from your form backend — a unique address for your form, like this one:
https://example.formbackend.com/api/submit/contactWith FormHook, endpoints are created automatically the first time a submission arrives, so you can use any key you like (contact, quote, newsletter) without configuring anything first.
Second, point your existing form at it. If you already have a contact form, the change is a single attribute:
<form action="https://example.formbackend.com/api/submit/contact" method="post">
<input type="text" name="name" required />
<input type="email" name="email" required />
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>That's the whole form. Keep your existing styling, labels and validation — nothing else changes. Some form backends also accept JSON if you prefer to submit with fetch; the URL is the same.
Third, deploy your site and submit the form once to test it. The submission should appear in your dashboard within a second or two. You can then check your spam folder (form backends filter junk automatically), mark anything that slipped through, and export the lot to CSV whenever you want your data in a spreadsheet.
Things to get right
- Use method="post", not method="get" — GET puts form data in the URL and is wrong for messages.
- Give every field a name attribute. The browser sends name=value pairs, and the backend stores them under those names.
- Test on the live URL, not just locally — CORS and hosting rules only show up in production.
- Add a privacy note if you collect email addresses; most regions require telling visitors what you store.
When you outgrow a single form
The same pattern scales: one endpoint per form, all visible in one dashboard. A waitlist form, a newsletter signup and a contact page can each post to their own endpoint while you manage everything from a single login — and if you ever move off the form backend, your data comes out as CSV, not trapped in a proprietary format.
Want forms without the backend?
FormHook is a form backend for static sites and Jamstack apps: point your form at an endpoint, submissions land in a dashboard, spam is filtered automatically. See the use cases.