You're building a static site locally on http://localhost:3000 or by double-clicking an
index.html file, and the contact form needs to actually work before you push it. Pointing
a form's action attribute at a hosted backend while you're still on localhost feels risky:
will the request even leave your machine? Will the service reject it because the domain doesn't match?
SimpleForm is a hosted form backend that receives submissions from any HTML form and emails or stores
them without you writing server code, and its origin handling is built around exactly this situation. The short answer is that testing from localhost works out of the box, and the rest of this article walks through why, plus the two response codes that explain almost every local testing failure.
What Happens When You Submit a Form From Localhost?
A plain HTML form with method="POST" and its action attribute set to a
SimpleForm endpoint URL works the same whether the page is served from localhost, a file
path, or your production domain. The browser sends the request to SimpleForm's servers directly; nothing
about the request depends on where the HTML happens to be sitting. What changes between environments is
the Origin header the browser attaches to the request, and that header is the only thing
SimpleForm uses to decide whether to accept or reject a submission.
How Does SimpleForm Decide Which Origins to Allow?
Every form has an optional "allowed domains" setting in the dashboard: a list of origins, one per
line, that the form will accept submissions from. If you leave it blank, any origin is accepted. If you
fill it in with your production domain only, a request from a different origin is rejected with a 403
response. The detail that matters for local testing is what happens when there's no Origin
header at all: SimpleForm still accepts the request. Privacy-focused browsers, some proxies, and a number
of local dev setups strip the header entirely, and rather than block those submissions, SimpleForm treats
a missing header as passing. That's also, incidentally, why a stripped header in production never silently
costs you a lead.
How Do You Test a Form Endpoint Before You Deploy?
You don't need a second, disposable endpoint for local testing. The same token you'll use in production works from localhost, as long as you haven't locked the form down to a single allowed domain yet.
- Create the form in your dashboard and copy its endpoint URL, something like
https://simpleform.dev/f/abc123xyz. - Set that URL as the
actionattribute on your local HTML form, and run your site the way you normally do — a dev server, or the file opened directly. - Leave the "allowed domains" field empty for now, so both your localhost origin and your eventual production domain can reach the endpoint.
- Submit a real test entry with a name, email, and message, then check your inbox and the submissions list in the dashboard.
- Once you've confirmed the fields arrive intact, add your production domain to "allowed domains" and submit the same test again after you deploy, to confirm the locked-down form still accepts your real site.
Local Testing vs. Production Testing: What's the Difference?
The request path is identical; only the origin and, usually, your intent are different. The table below lays out what actually changes.
| Aspect | Localhost testing | Production |
|---|---|---|
| Origin header sent | Often missing or a localhost value | Your real domain |
| Allowed domains setting | Leave blank, or include the dev origin | Your production domain only |
| Redirect URL | Points nowhere useful yet | Set to a real thank-you page |
| Rate limit | Same 10 submissions per IP per hour | Same 10 submissions per IP per hour |
Notice the rate limit row: SimpleForm doesn't distinguish a "test mode" from a "live mode". Every submission, wherever it comes from, counts against the same IP-based rate limit and the same monthly plan limit. That's a deliberate simplicity trade-off, and it's the point where hand-rolling your own mock backend stops being worth it — you'd be rebuilding honeypot handling, rate limiting, and email delivery just to get a realistic test. SimpleForm's documentation covers each of those pieces in more detail if you want to see the exact request and response shapes before you wire anything up.
How Do You Test AJAX Submissions Locally?
If your form uses fetch instead of a normal page redirect, local testing works the same
way, with one extra header. Add Accept: application/json to the request, and the endpoint
returns a JSON body instead of issuing a redirect, which is what lets you show a success message without
reloading the page:
document.querySelector('#myForm').addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target;
const res = await fetch(form.action, {
method: 'POST',
headers: { 'Accept': 'application/json' },
body: new FormData(form),
});
const data = await res.json();
if (data.success) form.reset();
});
This is worth testing locally specifically, not just assuming it matches your redirect-based test,
because a missing or wrong Accept header is the most common reason a developer sees a blank
page or a browser download prompt instead of the JSON response they expected. Confirm the header is set
correctly, and check the response in your browser's network tab, before you assume the endpoint itself is
broken.
What If Your Local Requests Get Rejected?
Two response codes explain almost every local testing failure. A 403 means the form's allowed domains
list is set and your current origin isn't on it — add the origin or clear the list while you're testing.
A 429 means you've hit the rate limit of 10 submissions per IP per endpoint per hour, which is easy to do
by resubmitting the same test form repeatedly while debugging. Wait out the hour or create a second form
for testing if you expect to submit that often. A 404 usually means the token in your action
attribute was copied wrong or the form was deleted.
Is It Safe to Point Local Testing at My Production Endpoint?
The honest objection here is that testing against your real endpoint means test submissions land in the same inbox and dashboard as real leads, and on the Free plan you only get 100 submissions and 3 forms a month to work with. If that mixing bothers you, create a second form in the dashboard specifically for testing — it costs nothing extra on any plan and keeps your test data separate — and delete or ignore it once your production form is confirmed working. What you don't need is a separate mock backend or staging service just to prove the request shape is correct, since the real endpoint behaves identically in both places. The same logic applies if your form includes a file input or a Pro-plan webhook: test them against the real endpoint too, because a mock backend won't tell you whether your multipart encoding or your webhook's signature verification actually works.
Getting Your Form Live
Once your local test submission shows up with the right fields, add your honeypot input (any field
name starting with an underscore, such as _honeypot), set your production domain in
allowed domains, and deploy. If you haven't created your first form yet, sign up
for a free SimpleForm account — it takes about a minute, and the same endpoint URL you test with
locally is the one your live site will use.
Frequently asked questions
No. SimpleForm accepts a request with no Origin header at all, which is common from local dev servers and privacy-focused browsers, so you can submit a real test from localhost using the same endpoint URL you'll use in production.
Yes, if you've already added your production domain to a form's allowed domains list, a request from a different origin, including localhost, returns a 403 response. Leave the list blank or add your dev origin while you're still testing.
Yes. SimpleForm doesn't have a separate test mode, so every accepted submission, local or live, counts toward your plan's monthly limit and the 10-per-hour, per-IP rate limit on that endpoint.
Any input whose name starts with an underscore, such as _honeypot or _gotcha, is treated as a spam trap. Include one hidden field with that naming pattern in your local test form so your production behavior matches what you tested.
Yes, as long as allowed domains is left blank or includes both origins. If you'd rather keep test submissions out of your real inbox, create a second form in the dashboard just for testing at no extra cost.