Static sites lack server-side form processing by definition — but functional contact forms are still achievable through a few practical approaches. This guide covers the main options.
The Core Challenge
An HTML form needs somewhere to submit data to — a static site has no server-side code to receive and process that submission (send an email, save to a database) without adding some form of backend, however minimal.
Option 1: A Lightweight Serverless Function
exports.handler = async (event) => {
const { name, email, message } = JSON.parse(event.body);
await sendEmail(name, email, message);
return { statusCode: 200, body: JSON.stringify({ success: true }) };
};
A minimal serverless function (deployable to various providers, or self-hosted, see How to Build and Secure a REST API on a VPS for a small dedicated endpoint on your own VPS) handles just the form submission, without needing a full traditional backend for your entire site.
Option 2: A Small Dedicated Endpoint on Your VPS
app.post('/api/contact', async (req, res) => {
const { name, email, message } = req.body;
await sendMail({ to: '[email protected]', subject: `Contact from ${name}`, text: message });
res.json({ success: true });
});
Since your static site is likely already hosted on a VPS, running one small API endpoint alongside it (see How to Send Email from a VPS Without Getting Blacklisted for the email-sending considerations) is a genuinely simple, self-hosted option.
Option 3: A Dedicated Form-Handling Service
Several third-party services exist specifically for handling static site form submissions — your form action points directly to their endpoint, and they handle spam filtering and email delivery; simplest option if you'd rather not build/maintain even a minimal backend yourself.
Basic Form HTML (Works with Most Approaches)
<form action="/api/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>
Handling the Submission with JavaScript (For a Better UX)
form.addEventListener('submit', async (e) => {
e.preventDefault();
const response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(Object.fromEntries(new FormData(form))),
});
if (response.ok) showSuccessMessage();
});
A JavaScript-handled submission avoids a full page reload, providing a smoother user experience than a traditional form POST-and-redirect flow.
Adding Spam Protection
See How to Add CAPTCHA and Rate Limiting to a Contact Form to Stop Spam — contact forms are a common spam target regardless of which backend approach you use; add appropriate protection (honeypot fields, rate limiting, or CAPTCHA) rather than leaving the endpoint unprotected.
Validating Input Server-Side
See How to Implement API Request Validation — validate submitted data server-side (email format, required fields, reasonable length limits) even if you have client-side validation too, since client-side checks can always be bypassed.
Common Errors
Form submissions never arrive despite no visible error — check that your backend endpoint's CORS configuration allows requests from your static site's actual domain, and verify email sending (see How to Send Email from a VPS Without Getting Blacklisted) is genuinely working, not silently failing.
Continue Reading
- How to Rate Limit an API with Nginx
- How to Build and Secure a REST API on a VPS
- How to Send Email via SMTP Relay from Your Application
Browse more articles in Static Site Hosting & Frontend Deployment.