How to add a contact form to a static site (no backend)
A step-by-step guide to adding a working contact form to any static site — HTML, Astro, Hugo, or plain pages — without writing or hosting a backend.
If you have ever wired up a <form> on a Netlify, GitHub Pages, or Cloudflare Pages site and watched it do nothing after clicking Submit, you have hit the fundamental limitation of static hosting: there is no server-side code to receive the POST.
This guide walks through exactly how to fix that — step by step — without writing or hosting any server yourself.
Why static sites can't process form submissions
When a visitor clicks Submit, the browser serialises the form fields and sends an HTTP POST request to whatever URL is in the action attribute. On a traditional web server, a running process — PHP, Node, Python, Ruby — receives that request, reads the fields, and decides what to do with them: send an email, write to a database, return a redirect.
Static hosts serve files from a CDN. There is no running process behind the scenes. If your form's action points at your own domain, the request lands on the edge node, finds no handler, and the visitor gets a 404 or a method-not-allowed error. Sometimes the browser just stalls.
The three escape hatches are:
- Deploy a backend. A Node, Python, or PHP server that handles the POST. This means infrastructure you have to provision, secure, keep running, and pay for — even if it handles twenty requests a month.
- Write a serverless function. AWS Lambda, Cloudflare Workers, or Vercel Edge Functions reduce the infrastructure burden. But you still write the email-sending code, deal with SMTP credentials, validate fields, and handle spam.
- Use a form backend. A dedicated service that accepts POSTs on your behalf, validates the fields, filters spam, stores the submission, and emails it to you. No code to write. No server to run.
For most static sites — portfolios, marketing pages, documentation sites, small business pages — a form backend is the right answer. It is the smallest possible surface to maintain, and you can have a working form in under ten minutes.
What a form backend does
A form backend is an HTTPS endpoint that accepts form submissions from any origin. You point your form's action attribute at it. The service:
- Receives the POST (standard
application/x-www-form-urlencodedormultipart/form-data) - Validates the field data
- Runs spam detection
- Stores the submission so you can review it in a dashboard
- Emails the data to the destination email addresses you control
From the visitor's perspective: they click Submit and land on a thank-you page. From your perspective: you get an email with their name, email address, and message. No server was harmed in the making of this contact form.
Step 1: Create a Formcrest form and verify your email
Sign up at formcrest.com/signup — no credit card required. Create a new form from the dashboard. You will be given a unique form ID, for example abc123. Your submission endpoint is:
https://api.formcrest.com/v1/submit/abc123
Before submissions are delivered, verify the destination email address you want them sent to. Formcrest sends a one-time confirmation link; click it and that address is ready to receive submissions.
Step 2: Write the HTML form
Point your form's action at your endpoint and set method="POST". The form below is a complete, working contact form:
<form action="https://api.formcrest.com/v1/submit/abc123" method="POST">
<label>
Name
<input type="text" name="name" required />
</label>
<label>
Email
<input type="email" name="email" required />
</label>
<label>
Message
<textarea name="message" required></textarea>
</label>
<!-- Honeypot: hidden from users, irresistible to bots -->
<input
type="text"
name="_honey"
style="display:none"
tabindex="-1"
autocomplete="off"
/>
<button type="submit">Send message</button>
</form>
Replace abc123 with your real form ID from the dashboard. That is the only site-specific change needed. The full HTML installation reference is at /docs/installation/html.
Step 3: Every field you want captured needs a name attribute
Formcrest reads the POST body and stores every named field verbatim — there are no required field names on the service side. Name a field phone-number and that is exactly what shows up in the email and the dashboard.
A few practical tips:
- Use lowercase, hyphen-separated names (
first-name, notfirstNameorFirst Name) - Do not use names beginning with
_for your own fields — Formcrest reserves the underscore prefix for internal fields like_honey requiredin the HTML adds client-side validation; it is the browser's job to enforce it before the POST is sent
Step 4: Set a custom success redirect
By default, Formcrest redirects a successful submission to its hosted thank-you page. That works, but most sites want to send visitors to their own branded confirmation page.
Configure the success URL in your form's settings in the dashboard. Once set, successful submissions redirect there. You can also configure an error URL; failed submissions redirect there with a ?error=CODE query parameter so you can show a relevant message. Both redirects are available on the free plan.
Step 5: The honeypot field — and why it matters
The _honey field you saw in the HTML above is a spam trap. Real visitors never see it because it is hidden with CSS. Automated bots, however, typically fill in every field they encounter. When the _honey field arrives with a value, Formcrest accepts the submission (giving the bot a normal success response so it gets no signal it was caught) but flags it as spam and never delivers it to your inbox.
Flagged submissions are stored in the dashboard so you can review them if needed. They never count against your monthly submission quota.
Keep the field hidden with style="display:none" rather than type="hidden" — bots can distinguish a genuinely invisible field from one that is programmatically hidden. Set tabindex="-1" to remove it from the keyboard tab order and autocomplete="off" so browsers do not auto-fill it.
The fetch alternative: no page reload
If you want a smoother experience — an inline success message without a full page navigation — intercept the submit event and send the data with fetch. Add the Accept: application/json header to tell Formcrest you want a JSON response instead of a redirect.
const form = document.querySelector('#contact-form');
form.addEventListener('submit', async (event) => {
event.preventDefault();
const response = await fetch('https://api.formcrest.com/v1/submit/abc123', {
method: 'POST',
headers: { Accept: 'application/json' },
body: new FormData(form),
});
const result = await response.json();
if (response.ok && result.ok) {
document.querySelector('#success').hidden = false;
form.reset();
} else {
document.querySelector('#error-msg').textContent = result.message;
}
});
Build FormData from the form element — the browser sets the correct Content-Type automatically, and the _honey field is included with everything else. A successful submission returns { "ok": true, "submissionId": "sub_abc123" }. For a fuller walk-through including error field handling, see /docs/installation/javascript.
Where submissions go, and how long they are kept
When a submission passes validation and spam checks, Formcrest stores it in the dashboard and emails all the captured fields to your verified destination email. The email includes the submission timestamp and a link to the dashboard entry.
Retention periods depend on your plan: 30 days on Free, 90 days on Pro, and unlimited on Agency. CSV export is available on every plan, so you can always download a copy before any retention window closes.
A quick note on spam at higher volume
The honeypot catches the majority of automated spam on low-traffic sites. If you are running a form on a page that gets significant traffic — or a page that appears in a public directory bots scan frequently — you may want to also enable Cloudflare Turnstile. It is a privacy-friendly challenge that is invisible to most real visitors and available on the free plan. Formcrest also applies automatic rate limiting at the edge; burst attacks are rejected before they reach your quota.
Ready to go
Adding a contact form to a static site takes less than ten minutes:
- Create a Formcrest form and verify a destination email address
- Copy the endpoint URL into your form's
actionattribute - Add the
_honeyhoneypot field - Optionally set a custom success redirect
Start for free — no credit card required. The Free plan includes 500 submissions per month, which is enough for most personal sites, portfolios, and small businesses. When you outgrow it, upgrading to Pro or Agency does not require changing a single line of your form HTML.
Ship your form in minutes
Point any HTML form at Formcrest — validation, spam filtering, and delivery included. Free for 500 submissions a month, no credit card.
Get started freeKeep reading
How to send HTML form data to email (without a backend)
Five ways to send HTML form submissions straight to your email — from mailto links to a form backend — with the spam, deliverability, and GDPR tradeoffs of each, plus copy-paste code.
Spam protection for HTML forms: honeypots, Turnstile, and rate limiting
A practical guide to stopping form spam — honeypot fields, Cloudflare Turnstile, and rate limiting — and how to layer them without hurting conversions.
How to handle form submissions without a server
What actually happens when an HTML form is submitted, and how a form backend lets you collect, validate, and route submissions without running any server code.