The Static Site Forms Guide For Non-Developers

A plain HTML form on a static host quietly loses every message. Here are the static site forms options that work, what stops spam, and where entries land.
The OxyPages Team · · 15 min read
You pasted a contact form onto your page, uploaded the site, filled the form in yourself as a test and pressed Send.
The page blinked. The fields went empty. Nothing arrived.
Nobody warned you, because there was nothing wrong with the HTML. A form tag is a box that collects answers. It has no opinion about what happens to them, and on a plain static site there is nobody standing behind it to catch them.
That is the whole of the static site forms problem in one sentence: the page can ask the question, but by default nothing is listening for the answer.
So this is the map for fixing it without learning a backend language. What actually happens when a visitor presses Send, the four real ways to catch a submission and what each one costs you, the spam that will absolutely arrive, where the entry should land afterwards, and how to count who visited without pasting a tracking script or bolting a cookie banner onto your homepage.
By the end you will know which of the four options is yours, and what to test before you put a real phone number behind it.
TL;DR: A plain HTML form on a static host collects data and throws it away, because a static server hands out files and never runs code. You have four choices: a handler built into your host, a third-party form endpoint, an embedded form from another product, or mailto:, which loses most submissions and should not really count. Whichever you pick, you still have to decide three things: what the visitor sees after Send, who gets told, and where the record lives. Add spam protection before launch rather than after, and count visitors on the server so there is no cookie banner to write.
Why A Plain HTML Form On A Static Site Does Nothing
A static host serves files. It hands the browser the exact HTML you uploaded and then stops thinking, so there is no program on the other end to receive what somebody typed.
Here is the form nearly everyone starts with:
<form>
<input name="email" type="email" required>
<textarea name="message"></textarea>
<button type="submit">Send</button>
</form>
That is valid HTML. It renders properly, it refuses a badly typed email address, and it loses the message.
The cause is the missing action. With no action, the browser submits the form back to the page it is already on. The default method is GET, so the field values get stuck on the end of the address and the page reloads looking identical. To the visitor that looks like a glitch. To you it looks like nobody ever wrote in.
A working form needs three things and you already have one of them:
- A page that collects the answers. You have this. It is the HTML above.
- A receiver. Something at a real web address that accepts the submission, checks it did not come from a robot, and stores it.
- A destination. Somewhere you will actually look: an inbox, a dashboard, a spreadsheet.
Everything below is about the second and third. If you would rather have the whole job as an ordered checklist, the full order to configure a form in walks the same ground step by step.
Your Static Site Forms Have Four Real Options
There are four, and the fourth is on the list mainly so you can rule it out: a handler built into your host, a third-party form service, an embedded form that somebody else hosts, or a mailto: link.
1. A Handler Built Into Your Host
Some static hosts run the receiver for you. The form posts to them, and entries appear in the same dashboard as your files.
This is usually the least work, because there is no endpoint to copy and no second account to open. On OxyPages it is one attribute on the form tag:
<form data-oxy-form="contact">
<input name="email" type="email" required>
<textarea name="message"></textarea>
<button type="submit">Send</button>
</form>
The value is just a name for the form, so entries get grouped under it. Two rules ride along and both catch people. Every input, select and textarea needs a name attribute or that field is not captured at all. And file uploads are not supported, so a file input records the filename and discards the file.
The trade is portability. A built-in handler is your host's feature, so moving the site means rewiring every form. Netlify Forms has the same shape and the same catch: it stops working the moment the site is deployed somewhere else. MDN on sending and retrieving form data
2. A Third-Party Form Service
Formspree, Basin and services like them hand you an endpoint address. You paste it into the form's action, and they do the rest: store the submission, email it to you, filter the obvious junk.
<form action="https://the-service.example/f/your-endpoint-id" method="post">
This works on any host, which is the entire point of it. Move the site to a different provider and the form keeps running, because nothing about it depends on where the page is served from.
What you give up is another account, another dashboard and another free tier to watch. These services meter by submission, and free allowances tend to be small enough that one spam wave can eat a month. Read the current numbers on the service's own pricing page rather than trusting a blog post, mine included, because those numbers move.
3. An Embedded Form Somebody Else Hosts
Build the form in Google Forms, copy the iframe embed code, paste it into your page. Responses collect in a Google Sheet, spam handling is Google's problem, and there are no keys to configure.
It is the fastest route to something that works, and I would use it for a one-off survey or an event signup without thinking twice.
For a business contact page I would not. The native embed carries Google's own look and does not let you remove the branding, so the form announces that it came from somewhere else. You also get a fixed-height frame to guess at, which is where that awkward inner scrollbar on phones comes from.
4. Mailto, And Why It Is Barely An Option
Setting action="mailto:you@example.com" does not send anything. The browser packs the fields into a draft message and hands it to whatever mail program the visitor's device has set as its default.
Three things go wrong, in order of how often. Plenty of devices have no default mail program at all, so the click does nothing visible. If a draft does open, the visitor still has to press Send themselves, and a good share of them will not. And what lands in that draft is frequently a wall of percent-encoded text instead of a readable message, because form fields are packed for a web server to unpack, not for a human to read. It is not a supported way to submit a form; the specification only recognises get, post and dialog. MDN's reference for the HTML form element
Nothing is stored anywhere either, so you cannot even count how many people tried. And the address sits in your page source in plain text for every scraper that comes past.
Use mailto: for a plain "email me" link if you want one. Never as the destination of a form whose submissions you care about.
The Four Options Side By Side
| Approach | What you wire up | Moves to another host | Where entries land | The honest downside |
|---|---|---|---|---|
| Host's built-in handler | One attribute on the form tag | No, it is your host's feature | Your hosting dashboard, plus email | You rewire every form if you move |
| Third-party form service | An endpoint address in action | Yes, unchanged | The service's dashboard, plus email | Another account and another free tier to watch |
| Embedded Google Form | Paste an iframe | Yes, unchanged | A Google Sheet | Looks like Google, not like your site |
| Mailto link | One action value | Yes, and it fails everywhere equally | Nowhere. A draft on the visitor's device | Most people never actually send it |
The decision is mostly about how likely you are to move. Take the built-in handler if your host has one and you plan to stay. Take a form service if you run several sites on different providers or you want submissions flowing into other tools. Take an embedded Google Form when the form matters more than the page around it. Do not take mailto.
The Spam Will Arrive, So Plan For It Now

Bots find forms by crawling for the form tag, not by finding your site through search, so a page nobody has visited yet still gets hit. The first junk submission usually shows up within days of publishing.
Three layers stop nearly all of it, and you want all three.
A honeypot. A hidden field that a human never sees and never fills in. Anything that fills it is automated. A good handler adds one for you with no setup, keeps the entry so you can see what was caught, and marks it as spam instead of emailing it to you.
A captcha. This is the layer that decides whether a submission is accepted at all. You register your site with a provider, get two keys, and save them in your handler's settings. Some hosts make this optional. Others, OxyPages included, reject every submission until the keys are saved, which is the single most common reason a form on a brand new site looks broken.
A rate limit. Repeat submissions from the same connection get slowed down or refused. You get this for free from most handlers, and it is also why your fourth test submission in a row sometimes fails when the first three worked.
There are two captcha providers worth your time, and they behave differently:
| Cloudflare Turnstile | Google reCAPTCHA v3 | |
|---|---|---|
| Account you need | A Cloudflare account | A Google account |
| What the visitor sees | A small widget, no picture puzzles | Nothing at all |
| How it decides | Quiet browser checks in the background | A score based on how the visitor behaved |
| Set-up | Create a widget for your hostname | Register your domain in Google's console |
| Best when | You want the simplest option that annoys nobody | You already live in Google's tools |
Turnstile is free at any volume and needs no Google account, which is why it is the one I point people at first. get your Turnstile keys is the three-minute version. If you would rather stay inside Google, get your reCAPTCHA v3 keys covers that console instead.
Whichever you pick, register every address people will actually use. Both providers check the hostname the widget is loaded on, so keys registered for your free subdomain and not for your custom domain will refuse to validate on the domain, and the form looks broken on the only address that matters.
Where Does The Submission Actually Go?
Three places, and you should decide all three before you publish: what the visitor sees, who gets told, and where the record lives.
What The Visitor Sees After Send
By default a good handler submits in the background, clears the fields and shows a short line of text near the button. The page does not reload.
You normally get three ways to change that. With the built-in handler above they are attributes on the same form tag:
<form data-oxy-form="contact" data-oxy-success="Thanks, we reply within a day.">
<form data-oxy-form="contact" data-oxy-redirect="/thanks.html">
<form data-oxy-form="contact" data-oxy-success-selector="#thanks">
The first swaps the message. The second sends the visitor to a page you wrote. The third hides the form and reveals an element already sitting on the page with the hidden attribute on it. There is a matching data-oxy-error for the text shown when a submission fails, and it is worth writing, because the default is generic by necessity.
Send people to a real page if the form matters. A thank-you page is where you say what happens next and how long it will take, and it is the cleanest way to count completed submissions, because it is the one address a visitor can only reach by finishing.
Who Gets Told
An email per submission is what everyone expects, and most handlers do it. Read the fine print on two things: which address it goes to, and how many you get.
On a host-run handler the notification usually goes to your account's login address and cannot be pointed somewhere else per form, so a shared team inbox means changing the account email or setting up forwarding. Notification emails also tend to be metered by plan while storage is not, so check what each plan includes rather than assuming.
Here is the part worth knowing before it bites: on a decent service, running out of email allowance does not lose anything. Submissions keep being stored and only the emails pause. But if your inbox is the only place you ever look, you will quietly conclude that the enquiries stopped.
Where The Record Lives
Email is a notification, not a database. Somebody archives the thread and the enquiry is gone.
Keep the list somewhere you can sort and search: the handler's own entries page, a spreadsheet, or both. A spreadsheet is the one small teams actually keep up with, and send your form entries to a Google Sheet covers the Apps Script route, which works alongside whatever handler you chose rather than replacing it.
Every handler keeps a finite number of entries, and the ceiling is worth knowing before you rely on it. Here the newest 5,000 entries per website are kept. That is years of contact form traffic and a few months of a busy signup, so treat the spreadsheet as the archive and the dashboard as the inbox.
How To Count Visitors Without A Cookie Banner

Count on the server rather than in the browser. The host tallies each page as it serves it, so there is no tracking script in your HTML, no cookie in the visitor's browser and nothing for anyone to consent to.
That is what cookieless analytics means in practice, and it changes the trade in three specific ways:
- No banner. If nothing is stored that identifies a person, there is nothing to ask permission for. Check with whoever writes your privacy policy if you work in a regulated field, but for a brochure site this removes the pop-up entirely.
- Ad blockers cannot hide anybody. A browser script is a file a blocker can refuse to load. A tally kept on the way out is not.
- Bots come out of the number. Crawlers, uptime monitors and AI scrapers request pages constantly. Server-side counting can exclude them, along with requests for images, CSS and JavaScript, so a view means a real browser opening a real page. OxyPages does this by default and reports views, unique visitors, top pages, countries, referrers, devices and browsers.
What you give up is real, and I would rather say it plainly. Counting this way will not follow one person across weeks, will not build funnels or custom events, and will not attribute an ad click to an enquiry three visits later. If you run paid campaigns and need attribution, you still want Google Analytics, and the two can live side by side.
Expect them to disagree, and expect the gap to be wide. Google Analytics misses everyone running a blocker and counts by its own session rules. Server-side counting misses nobody and strips the bots out. Neither number is wrong, they are measuring different things, so pick one as your source of truth and stop reconciling them.
One more thing to plan around: the window is 90 days here, so the dashboard answers "is this page working now" rather than "how did last spring compare". If you need a year over year view, copy the monthly numbers somewhere of your own before they roll off.
The Rest Of The Path A Lead Takes
A form is one step in a chain, and the chain usually breaks at the boring parts.
- A dead link loses more enquiries than a broken form. A mistyped URL on a flyer, a page you renamed, an old link somebody saved. put a real 404 page in place keeps those visitors moving instead of bouncing off an error screen.
- Some people will never fill in a form. On a phone, a chat button converts people that a form does not. add a floating WhatsApp button is a snippet and no plugin.
- A printed QR code is a form entry point too. Put it on the menu, the flyer or the van, and point it at the page carrying the form, not at your homepage. Then check what it looks like when a phone camera opens it, because a code that lands on a slow page gets abandoned before the form is ever seen.
- A newsletter signup is a different job. Collecting addresses through a contact form gives you a list with no unsubscribe link and no sending reputation behind it. Use an email platform's own signup form, or push entries into one, and the legal parts come along with it.
What To Check Before You Trust It With A Lead
Do this once, properly, before you put the address on anything printed.
- Publish first, then test on the live address. Forms do not submit from a local file or an editor preview. This alone accounts for a lot of "it does not work" reports.
- Give every field a
name. No name, no data, and no error message to tell you. - Save your captcha keys before you announce the page. On a host that requires them, everything submitted before that moment is refused.
- Send a real test submission and time the email. Then check the spam folder, because the first notification from an unfamiliar sender often lands there.
- Confirm the entry appears in storage, not only in your inbox. If email is the only copy, one archive sweep and it is gone.
- Do the whole thing on a phone, on mobile data. Not the laptop that still has yesterday's version cached.
- Break it on purpose. Submit with the required field empty and read the failure text a real visitor would see.
- Come back in a month and test again. Free tiers reset, keys get revoked, domains get renamed. Testing your static site forms on a schedule takes two minutes and beats finding out from a customer.
What To Do Next
Pick the option that matches how likely you are to move hosts, wire it up, and then spend the extra ten minutes on the spam layer and the thank-you page.
That order matters. A form that works but fills with junk is worse than no form at all, because you will stop reading it after a fortnight and a real enquiry will be sitting in there.
Be straight about the ceiling too. If what you need is a member login, a booking calendar that knows what is available, or a page that reads data back out of a database, a static site is the wrong shape for it and OxyPages is the wrong tool with it. You want an application platform, and no amount of clever form wiring substitutes for one.
For everything else, which is most contact pages, enquiry forms, event signups and one-page sites, this is a solved problem and it costs you an afternoon at most.
If the site itself is still sitting in a folder on your desktop, that is the actual first step. drop your folder in and get a live address takes about a minute, and every option above works the same way once the page has a real address.
FAQ
Can A Static Website Have A Working Contact Form?
Yes. The page is static, but the receiver does not have to be. Either your host runs a form handler for you or you point the form at a third-party endpoint, and in both cases you write no server code of your own. The only setup that never works is a form with no destination configured at all.
Do I Need A Backend Or A Database To Collect Form Entries?
No. You need something at a real web address that accepts the submission and stores it, and that is exactly what a built-in handler or a form service is. You are renting the backend instead of writing one. A database only becomes necessary when visitors need to read data back out, such as a members area or a live booking calendar.
Why Does My Form Submit But Nothing Arrives?
Four causes cover almost all of it: the form has no action or handler attribute, the captcha keys were never saved, a field is missing its name, or the notification is sitting in your spam folder. Check them in that order, and always test on the published address rather than a preview.
Do I Need A Cookie Banner For A Site With A Contact Form?
Not for the form itself, and not for analytics that count on the server without setting cookies. A banner exists to ask permission for tracking stored in the visitor's browser. Add Google Analytics, an ad pixel or an embedded video that sets cookies and you are back to needing one.
How Often Should I Test A Form Once It Is Live?
Monthly, and after any change to your domain, your host or your captcha keys. Publish, submit a real entry from a phone, confirm the email arrives and the entry is stored, then read what the page shows after Send. Treating your static site forms as something that can silently break is the difference between finding out in a week and finding out when a customer asks why you never replied.