Add a contact form to a Cloudflare Pages site (no Functions required)
Cloudflare Pages is a great way to ship a static site — until someone asks for a contact form. Pages serves static assets; it has no built-in form handling. Cloudflare's own forms tutorial starts by having you write a Pages Function, because there's nothing native to receive the POST.
You have three real options. (We make SubmissionBuddy, which is option three — but here's the honest rundown of all of them.)
Option 1: Write a Pages Function yourself
Cloudflare's tutorial walks through creating functions/api/submit.js to catch submissions. Functions run on the Workers free tier's shared request quota (pricing), so cost isn't the issue.
The issue is that catching the POST is the easy 10%. You still need somewhere to put the submission (KV? D1? Airtable?), email notifications (now you're signing up for a mail API), spam filtering, retries when the mail call fails, and some way to browse what's been submitted. Each piece is a small project; together they're a backend — the thing you chose Pages to avoid.
Option 2: The Static Forms plugin
Cloudflare ships a Static Forms Pages Plugin that intercepts submissions from forms tagged with data-static-form-name. It hands you the parsed form data — and then you're back at option 1, because you still write the handler that stores it, mails it, and filters spam.
Option 3: Point the form at a hosted backend
This is the approach Cloudflare themselves document with their Formspree tutorial: keep the site fully static and POST the form to a service that does the backend part. Any form backend works this way, SubmissionBuddy included. Here's the SubmissionBuddy version.
1. Get an endpoint
Sign up free (100 submissions/month, no credit card), create a form, and you get a URL like:
https://api.submissionbuddy.io/f/your-form
2. Point your form at it
Drop this into any page of your Pages site — plain HTML, Astro, Hugo, whatever you build with:
<form action="https://api.submissionbuddy.io/f/your-form" 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 humans; bots that fill it are silently dropped -->
<input type="text" name="_honeypot" style="display:none" tabindex="-1" autocomplete="off" />
<!-- where to send the visitor after a successful submit -->
<input type="hidden" name="_redirect" value="https://your-site.pages.dev/thanks/" />
<button type="submit">Send</button>
</form>
That's the whole integration. No Function, no KV namespace, no mail API keys. Field names are up to you — there's no schema, so you can add or rename fields anytime without reconfiguring anything.
Prefer JavaScript? Use fetch
If you want to stay on the page and show inline success/error states:
const res = await fetch('https://api.submissionbuddy.io/f/your-form', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: form.name.value,
email: form.email.value,
message: form.message.value,
}),
})
if (res.ok) {
// 202 { "ok": true, "submission_id": "…" }
showSuccessMessage()
}
JSON, application/x-www-form-urlencoded, and multipart text fields are all accepted.
3. What happens to the submission
Every submission is written to immutable, durable storage before anything else happens to it, then flows through retryable queue stages: database write (so it's searchable and exportable in the dashboard), then an email notification to your team. A mail hiccup delays the notification; it never loses the lead. The full pipeline walkthrough has the details.
Spam handling is on by default: per-IP rate limiting and the honeypot field above are included on every plan, and paid plans can add an invisible, privacy-friendly CAPTCHA for the determined bots.
Which option should you pick?
- You want to learn Pages Functions, or have unusual requirements → option 1.
- You already have storage + email + spam handling built and just need the glue → option 2.
- You want the form working this afternoon and never want to think about it again → option 3.
Cloudflare documentation links checked 2026-07-12. Code samples verified against the live SubmissionBuddy API the same day.