Static sites have no backend to receive a POST request. That doesn’t mean they can’t have forms. It means the processing has to happen somewhere else, and where you route it depends on what the form actually needs to do.
Why the old answer stopped working
Before static hosting was common, you’d drop a PHP script on a shared server and have a working contact form in an afternoon. That still works on traditional hosting. Most modern static sites, though, live on a CDN, and the static hosts that make them fast (Netlify, Vercel, GitHub Pages, Cloudflare Pages) have no compute layer behind them by default. There is nothing listening for a POST.
The good news: there are mature, well-supported options. The tricky part is choosing the right one, because the wrong choice adds operational weight without much payoff.
Option 1: Hosted form services
Services like Formspree, Formspark, Netlify Forms, and Getform exist for this exact purpose. You point a form’s action attribute at their endpoint, they capture the submission, and they forward it to your email, a webhook, or a dashboard.
Setup takes minutes. There is no server code to write, spam filtering is included, and free tiers cover most low-volume contact forms. You handle the form markup and styling on your end however you like. If you are already using Tailwind CSS for the site, the same utility classes apply to form fields with no extra integration work.
The trade-offs:
- Control. Submissions live in a third-party database. If you need to validate a field against your own records, write to a CRM, or trigger a workflow, hosted services get awkward fast. Most support webhooks, but you end up writing logic anyway.
- Cost at scale. Free tiers cap at 50–100 submissions per month on most plans. A lead-generation form at volume adds up.
- Vendor dependency. Switching services means updating every form’s
action. Not painful, but not zero cost.
For a contact page or newsletter sign-up, a hosted service is the right call. Get it done and move on.
Option 2: Serverless functions
If you are deploying to Netlify, Vercel, or Cloudflare, you have access to serverless functions (or edge functions) in the same project. A form can POST to /api/submit, which triggers a small function that validates the input and does exactly what you need: send an email via Resend or Postmark, write to a database, call a third-party API.
This is more work up front. You are writing and owning code. But you control the logic completely, costs scale cheaply even at high volume, and no third party sits between you and your submission data.
A minimal Netlify Functions setup looks like this:
// netlify/functions/contact.js
export async function handler(event) {
const body = JSON.parse(event.body);
// validate, send email, write to db
return { statusCode: 200, body: JSON.stringify({ ok: true }) };
}
The form submits to /.netlify/functions/contact with fetch() or a plain <form action>.
One practical note: if you are building with Astro, Next.js, or another framework that has first-class API routes or server actions, reach for those before adding a separate function file. The framework abstraction is usually cleaner and better tested. Getting a form working is often one step in a larger production build, and if you are starting from a design file, the Figma-to-production workflow is where that handoff happens.
The question isn’t “how do I add a form to a static site.” It’s “what should own my form logic, and do I trust that vendor with that data?”
Option 3: Direct API integration
A third pattern works well when you already use a SaaS tool that can ingest data directly: skip the custom function and POST to that service’s API from the client. HubSpot’s Forms API takes only a public portal ID. Some newsletter platforms expose a public subscribe endpoint. If the data’s destination is already a SaaS tool with a safe public-facing endpoint, there is no reason to proxy it through your own function.
The risk is key exposure. You cannot put secret API keys in client-side JavaScript. If the service only offers a private API, you need a serverless function in the middle to keep the key off the browser. Verify the CORS policy and any public endpoint documentation before building toward this pattern.
Spam: assume it arrives
Any public form attracts bots. Plan for it before launch, not after.
A honeypot field is the minimum viable defense. Add a hidden <input> that real users never see or fill. If a submission includes it, reject it. Most hosted services add one automatically. For a custom function, you wire it yourself in about five lines.
Rate limiting comes next. On a serverless function, cap submissions per IP per time window. Most hosting platforms have edge rate-limiting built in; use it rather than hand-rolling anything.
hCaptcha or reCAPTCHA v3 handles the rest. Invisible scoring doesn’t interrupt real users, adds minimal latency, and cuts bot traffic sharply.
File uploads: cost the decision early
Most hosted form services don’t support file uploads, or cap file sizes at a few megabytes. For a serverless function, you’d stream the file to object storage (Cloudflare R2, AWS S3, or similar) and store the reference. It is not complicated, but it is noticeably more code than a text-only form. If file uploads matter for your use case, factor that in before you’ve built the rest of the form around a different approach.
Choosing
Hosted service: contact form, newsletter sign-up, low-volume intake. No custom logic needed. Get it done in an afternoon.
Serverless function: custom validation, CRM routing, multi-step workflows, or any form where the submission is a real business process. You want to own the logic and the data.
Direct API integration: the data goes to a SaaS tool that exposes a safe public endpoint. Confirm that before you build toward it.
There is no penalty for starting with a hosted service and migrating to a function later. Most projects never need to.
How Strynal approaches form backends
At Strynal, form handling gets resolved during the architecture conversation, not as an afterthought. The choice depends on what the form actually does. A single contact form on a marketing site gets a hosted service. A lead intake form that feeds a CRM and triggers a follow-up sequence gets a serverless function with proper validation and logging.
The hosting environment shapes the decision too. Knowing which platform a site lands on (covered in more depth in the web hosting options guide) tells you which functions runtime is available and whether cold-start behaviour is worth thinking about.
Forms are one of those touchpoints where a small technical decision has disproportionate business impact. Route a lead to the wrong place, or lose a submission to a spam filter that was never tuned, and the cost is invisible: the lost contact never shows up in your funnel. That’s why we treat it as a first-class architecture decision.
Our websites and apps practice covers builds end to end, including the pieces that don’t show up in a design file. If you are unsure which form approach fits your project, it’s a short conversation with a clear answer.