Skip to content
Tech News
← Back to articles

Using Cloudflare Workers and reCAPTCHA v3 for a Static Site Contact Form

read original more articles
Why This Matters

This article highlights how integrating Cloudflare Workers with reCAPTCHA v3 enhances the security and spam prevention of static site contact forms, demonstrating a practical solution for developers using serverless architectures. It underscores the importance of leveraging serverless functions and security tools to maintain user engagement while protecting against spam, especially for static sites without traditional backend support.

Key Takeaways

Build a secure Eleventy (11ty) contact form using Cloudflare Pages Functions, reCAPTCHA v3, Resend, and as an alternative Cloudflare Turnstile to reduce spam.

Introduction I recently wrote a blog post that involved using a Cloudflare Worker to serve Brotli 11 compressed HTML rather than the standard uncompressed HTML file. Off the back of this post, I’ve decided to write one about my Cloudflare Worker setup that I use for my Contact Page. In order to stop the spam, I also integrated reCAPTCHA v3 into the form functionality. The form is pretty simple, it asks a visitor for their full name, email address and the message they with to send to me. The Worker then pulls together this information and sends an email to a custom email address that points to my personal email address. This allows me to easily filter messages that come from the site, which I then have the option to reply to if I so wish. It’s worth noting that this post was heavily inspired by Sia Karamalegos’s post from 2024 about migrating from Netlify to Cloudflare. I had the absolute please of meeting Sia in Amsterdam when I spoke at Performance.now() 2023, she was MC for my talk and I really appreciated her support when I was (quite frankly) freaking out with nerves before the talk! So why use a serverless endpoint for the contact form? Well, as I’ve mentioned many times before, this blog is a static site built using 11ty and hosted on the Pro plan of Cloudflare Pages. There simply is no backend to point the POST method too, which is required to send the email.

Previous solutions I’ve used This isn’t the first time I’ve changed the way the contact form works on this blog. I’ve counted 5 alternatives that I have used over the years! Isn’t Git great for tracking history! I certainly wouldn’t be able to remember them all with my terrible memory! So, if you aren’t on Cloudflare, or you would rather not use a Worker for this, then there’s a list of perfectly viable alternatives below: Netlify Forms URL : Netlify Forms

: Netlify Forms Cost: Free+ (depending on the Netlify tier you choose) Netlify Forms is a built-in form handling service for sites hosted on Netlify. It allows you to collect and manage form submissions from static websites without building or maintaining a backend. Submissions can be viewed in the Netlify dashboard or forwarded via email or webhook. I still think it’s a real shame that Cloudflare doesn't offer a form setup that is as easy to integrate and use as Netlify Forms, although, if it did, I wouldn’t need to be writing this blog post! Cloudflare Pages Functions + MailChannels URL : https://developers.cloudflare.com/pages/functions/ & https://www.mailchannels.com/

: https://developers.cloudflare.com/pages/functions/ & https://www.mailchannels.com/ Cost: Free plan available ($10+ depending on volume-based pricing) When I initially migrated from Netlify to Cloudflare for hosting I used this setup, that was until MailChannels sunset its free email sending service for Cloudflare Workers users (me), at that point I started looking for other solutions. As you will see below. Formspree.io URL : https://formspree.io/

: https://formspree.io/ Cost: Free tier available ($15+ a month for paid plans) Formspree is a hosted form service that lets you collect form submissions without building your own backend server. You simply point your form at Formspree, and it handles sending submissions to your email address or other API endpoints, it comes with built-in spam protection and integrations for various workflows. Formspark.io (previously Submit Form) URL : https://formspark.io/

: https://formspark.io/ Cost: Free tier available ($9+ a month for paid plans) Formspark is a basic hosted form backend that lets you accept and manage form submissions without the need for your own backend server. You simply submit forms to Formspark, and it stores (or forwards) the data, with options for email notifications, webhooks, and spam filtering. Botpoison URL : https://botpoison.com/

: https://botpoison.com/ Cost: Free plan available ($4+ per month depending on bot verifications required) Botpoison is an invisible anti-spam and bot prevention service for web forms that blocks automated submissions without requiring CAPTCHAs or extra steps from users. It works by analysing form interactions and applying proof-of-work challenges and reputation checks to distinguish human traffic from bots. I started using it when I was still receiving contact form spam from my Formspark.io setup. Resend URL : https://resend.com/

: https://resend.com/ Cost: Free plan available ($20+ for paid plans) Resend is a developer-focused email delivery service, providing a simple API and Simple Mail Transfer Protocol (SMTP) interface for sending transactional and broadcast emails from your applications or website. It handles features like deliverability, bounce tracking, suppression lists, and email analytics, so a developer doesn't need to manage email infrastructure themselves and can be integrated with many platforms. Resend is currently part of my email workflow, as I will describe later in the post.

Overview of the setup My current setup is: Cloudflare Pages Functions (the Worker) + Google reCAPTCHA v3 + Resend for email. A high-level view of this workflow is: User populates the contact form with their details and message reCAPTCHA v3 runs and adds a short-lived verification token The form POSTs to /api/contact (via the Pages Function, that sits in functions/api/contact.js in the root of my repository The Pages Function, then: verifies the token with Google

... continue reading