Cloudflare Pages Functions and KV Store provide a powerful serverless solution for building contact forms without traditional backend infrastructure. This post details how to implement a secure, scalable contact form using these technologies.
Table of Contents
- Table of Contents
- Why Cloudflare Pages Functions and KV Store?
- Architecture Overview
- Implementation Guide
- Deployment
- Cost Analysis
- API Endpoints
- Security Best Practices
- Conclusion
Why Cloudflare Pages Functions and KV Store?
Traditional contact form solutions often require:
- Dedicated backend servers
- Database infrastructure
- Email server configuration
- Complex deployment pipelines
Cloudflare’s serverless stack eliminates these requirements while providing:
- Zero Infrastructure Management: No servers to maintain or scale
- Global Edge Network: Code runs close to users worldwide
- Built-in Security: DDoS protection, rate limiting, and CAPTCHA support
- Cost Efficiency: Pay-per-request pricing with generous free tier
Architecture Overview
The solution consists of four main components:
- Static Frontend: HTML form hosted on Cloudflare Pages
- Pages Function: Serverless handler processing form submissions
- KV Store: Distributed key-value storage for submission data
- Turnstile CAPTCHA: Bot protection and spam prevention
Implementation Guide
The complete working example code is available on GitHub: ivandachev-examples/cloudflare-contact-form
Step 1: Setting Up Cloudflare Pages
First, create a new Cloudflare Pages project:
# Create project directory
mkdir contact-form-app
cd contact-form-app
# Initialize npm project
npm init -y
# Install dependencies
npm install -D wrangler
Step 2: Creating the Contact Form
Create a simple HTML form in public/index.html
:
View on GitHub: index.html
Step 3: Implementing the Pages Function
Create the serverless handler in functions/api/contact.js
. This implementation includes:
- CORS support for cross-origin requests
- Rate limiting (5 requests per minute per IP)
- Configurable validation with environment variables
- API endpoints - POST for submissions, GET for retrieval
- Turnstile CAPTCHA verification for bot protection
View on GitHub: contact.js
Key features in the code:
- CORS headers configuration with allowed origins
- Rate limiting using KV with automatic expiration
- Input validation with configurable min/max lengths
- API key authentication supporting both query parameter and header
- Turnstile token verification with server-side validation
- Comprehensive error handling with appropriate HTTP status codes
Step 4: Configuring KV Store
Create a wrangler.jsonc
configuration file with KV namespace binding and environment variables:
View on GitHub: wrangler.jsonc
The configuration includes:
- KV namespace binding for storing submissions
- Allowed origins for CORS
- Configurable validation limits
- Comments for setting up the API key secret
Create the KV namespace and set up authentication:
# Create KV namespace using npm script
npm run kv:create
# Copy the generated ID to wrangler.jsonc
# Generate a secure API key
openssl rand -hex 32
# Set the API key as a secret
npx wrangler pages secret put API_KEY --project-name contact-form-app
# Enter the generated key when prompted
# Verify the secret was uploaded
npx wrangler pages secret list --project-name contact-form-app
Step 5: Configure Cloudflare Turnstile
Turnstile provides CAPTCHA protection to prevent spam and bot submissions:
- Create a Turnstile site in your Cloudflare dashboard:
- Navigate to Turnstile in the sidebar
- Click Add Site
- Configure your site with your domain(s)
- Choose widget mode (Managed or Invisible)
- Copy the Site Key and Secret Key
- Update your configuration:
- Set
TURNSTILE_SITE_KEY
inwrangler.jsonc
- Update the site key in your HTML form
- Set
-
Set the secret key:
npx wrangler pages secret put TURNSTILE_SECRET_KEY --project-name contact-form-app # Enter your Turnstile secret key when prompted
- Widget customization options:
<div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY" data-theme="light" data-size="normal"></div>
For detailed Turnstile setup instructions, see the TURNSTILE_SETUP.md guide in the example repository.
Deployment
Deploy your contact form application:
# Login to Cloudflare
wrangler login
# Deploy to Cloudflare Pages
npm run deploy
For local development:
# Run development server on http://localhost:8788
npm run dev
The example repository also includes development tools for code quality:
# Format code with Prettier
npm run format
# Run ESLint for JavaScript linting
npm run lint
# Run Stylelint for CSS linting
npm run lint:css
Cost Analysis
Cloudflare’s pricing model for this solution:
- Pages: Free for unlimited requests
- Functions: 100,000 requests/day free, then $0.50/million
- KV Storage: 100,000 reads/day free, 1,000 writes/day free
- KV Operations: $0.50/million reads, $5.00/million writes after free tier
For a typical contact form receiving 1,000 submissions per month:
- Monthly Cost: $0 (within free tier)
- Scale Capacity: Can handle millions of submissions without infrastructure changes
API Endpoints
The contact form provides these endpoints:
Submit Contact Form
- POST
/api/contact
- Body: JSON with
name
,email
,message
, andcf-turnstile-response
fields - Requires valid Cloudflare Turnstile token
- Rate limited to 5 submissions per minute per IP
Get Submissions (requires API key)
- GET
/api/contact?api_key=YOUR_API_KEY
- GET
/api/contact
with headerX-API-Key: YOUR_API_KEY
- Query parameters:
limit
: Number of results per page (1-1000, default: 100)cursor
: Pagination cursor for next page
- Returns JSON response with:
submissions
: Array of submission objectslist_complete
: Boolean indicating if there are more resultscursor
: Cursor for next page (null if no more results)count
: Number of submissions in current page
Check API Status
- GET
/api/contact
- Returns API status (no authentication required)
Security Best Practices
- CAPTCHA Protection: Cloudflare Turnstile prevents bot submissions
- Input Validation: Always validate and sanitize user input
- Rate Limiting: Implement per-IP rate limiting (5 requests/minute)
- CORS: Configure appropriate CORS headers for allowed domains
- API Security: Use API keys for data access endpoints
- Encryption: Use HTTPS for all communications
- Data Retention: Implement automatic data expiration policies
- Token Security: Turnstile tokens expire after 5 minutes and are single-use
Conclusion
Cloudflare Pages Functions and KV Store provide a robust, scalable solution for contact forms that eliminates traditional infrastructure complexity. This serverless approach offers:
- Global Performance: Edge deployment ensures low latency worldwide
- Automatic Scaling: Handle traffic spikes without configuration
- Cost Efficiency: Pay only for what you use
- Developer Experience: Simple deployment and maintenance
The combination of Pages Functions for compute and KV Store for persistence creates a complete backend solution that’s perfect for contact forms and similar applications requiring simple data collection and storage.
Have you built serverless applications with Cloudflare? Share your experiences and tips in the comments below!