A simple yet powerful Cloudflare Worker that handles contact form submissions and routes emails to your custom address — with optional Google reCAPTCHA Enterprise protection.
- 📬 Email Routing — Forward contact form submissions directly to your inbox
- 🎨 React Email Templates — Beautiful, customizable HTML emails using React/TSX
- 🛡️ reCAPTCHA Enterprise — Optional bot protection with Google reCAPTCHA v3
- 🌐 CORS Support — Configure allowed origins for your frontend
- ⚡ Serverless — Runs on Cloudflare's global edge network
- 🔒 Secure — No backend server needed, runs entirely on Cloudflare
Before you begin, ensure you have:
- ✅ A Cloudflare account with a domain
- ✅ Cloudflare Email Routing enabled for your domain
- ✅ Node.js (v18 or higher) installed
- ✅ Wrangler CLI installed
- ✅ (Optional) Google reCAPTCHA Enterprise project
# Clone the repository
git clone <your-repo-url>
cd noreply-mail-worker
# Install dependencies
npm installEdit the wrangler.json file with your settings:
# Login to Cloudflare (first time only)
wrangler login
# Deploy to Cloudflare Workers
npm run deployYour worker will be available at: https://noreply-mail-worker.<your-subdomain>.workers.dev
| Variable | Required | Description |
|---|---|---|
TO_EMAIL |
✅ | Default recipient email address for contact form submissions |
FROM_EMAIL |
✅ | The sender address (must use your verified domain, e.g., no-reply@yourdomain.com) |
AUTHORIZED_ORIGINS |
✅ | Comma-separated list of allowed origins for CORS |
RECAPTCHA_ENABLED |
❌ | Set to "true" to enable reCAPTCHA protection (default: "false") |
RECAPTCHA_PROJECT_ID |
❌ | Google Cloud project ID (required if reCAPTCHA enabled) |
RECAPTCHA_API_KEY |
❌ | reCAPTCHA Enterprise API key (required if reCAPTCHA enabled) |
RECAPTCHA_SITE_KEY |
❌ | reCAPTCHA site key for frontend (required if reCAPTCHA enabled) |
ENABLE_LOGGING |
❌ | Set to "true" for debug logging (default: "false") |
- Go to Cloudflare Dashboard → Email → Email Routing
- Enable Email Routing for your domain
- Add the destination email address in
allowed_destination_addressesinwrangler.json - Verify the destination email address when prompted
POST https://your-worker-url.workers.dev
Send a multipart/form-data or application/x-www-form-urlencoded POST request with the following fields:
| Field | Required | Description |
|---|---|---|
message |
✅ | Message content (passed to your email template) |
subject |
❌ | Email subject line (default: "New Contact Form Submission") |
to_email |
❌ | Override recipient email (defaults to TO_EMAIL env variable) |
recaptchaToken |
❌ | reCAPTCHA token (required if reCAPTCHA enabled) |
| custom fields | ❌ | Any additional fields defined in your DataType (see customization) |
💡 Add custom fields to
DataTypeinsrc/types.tsto extend the form data.
<form action="https://your-worker-url.workers.dev" method="POST">
<input type="text" name="subject" placeholder="Subject" />
<textarea name="message" placeholder="Your Message" required></textarea>
<button type="submit">Send</button>
</form>const formData = new FormData();
formData.append('subject', 'Contact Form Inquiry');
formData.append('message', 'Hello, this is a test message!');
// Optional: override recipient
// formData.append('to_email', 'other@example.com');
const response = await fetch('https://your-worker-url.workers.dev', {
method: 'POST',
body: formData,
});
if (response.ok) {
console.log('Email sent successfully!');
} else {
console.error('Failed to send email:', await response.text());
}| Status | Description |
|---|---|
200 |
Email sent successfully |
400 |
Invalid request (missing fields or invalid data) |
403 |
reCAPTCHA verification failed |
405 |
Method not allowed (only POST is accepted) |
500 |
Server error |
- Go to Google Cloud Console
- Create a new project or select an existing one
- Note your Project ID
- Navigate to APIs & Services → Library
- Search for "reCAPTCHA Enterprise API"
- Click Enable
- Go to Security → reCAPTCHA Enterprise
- Click Create Key
- Choose Score-based (v3) key type
- Add your domains
- Copy the Site Key
- Go to APIs & Services → Credentials
- Click Create Credentials → API Key
- Restrict the key to reCAPTCHA Enterprise API
- Copy the API Key
Update your wrangler.json:
"vars": {
"RECAPTCHA_ENABLED": "true",
"RECAPTCHA_PROJECT_ID": "your-project-id",
"RECAPTCHA_API_KEY": "your-api-key",
"RECAPTCHA_SITE_KEY": "your-site-key",
// ... other vars
}Add reCAPTCHA to your frontend:
<script src="https://www.google.com/recaptcha/enterprise.js?render=YOUR_SITE_KEY"></script>
<script>
async function submitForm(formData) {
// Get reCAPTCHA token
const token = await grecaptcha.enterprise.execute('YOUR_SITE_KEY', {
action: 'submit_contact_form',
});
formData.append('recaptchaToken', token);
const response = await fetch('https://your-worker-url.workers.dev', {
method: 'POST',
body: formData,
});
return response;
}
</script># Start local development server
wrangler devThe worker will be available at http://localhost:8787
noreply-mail-worker/
├── src/
│ ├── index.ts # Main worker code
│ ├── email-template.tsx # React email template (customize this!)
│ └── types.ts # TypeScript types (add custom fields here)
├── test/
│ ├── index.spec.ts # Test files
│ └── tsconfig.json # Test TypeScript config
├── package.json # Dependencies & scripts
├── tsconfig.json # TypeScript configuration
├── vitest.config.mts # Vitest test configuration
├── wrangler.json # Cloudflare Worker configuration
└── README.md # This file
Edit src/email-template.tsx to customize your email design. The template receives all form data as props:
import type { EmailTemplateFunction } from './types';
const Template: EmailTemplateFunction = (props) => {
return (
<html>
<body>
<h1>New Message</h1>
<p>{props.message}</p>
{/* Access custom fields: props.name, props.email, etc. */}
</body>
</html>
);
};
⚠️ Important: Always use inline styles in email templates. External CSS is not supported by email clients.
Extend the form data by editing src/types.ts:
/**
* Setup your custom DataType here
*/
type DataType = {
name: string;
email?: string;
phone?: string;
company?: string;
};
/**
* Do not edit this type definition
*/
export type __EmailWorkerType__ = {
recaptchaToken?: string;
subject?: string;
message: string;
to_email?: string;
} & DataType;Now you can:
- Send these fields from your frontend form
- Access them in your email template via
props.name,props.email, etc. - Return an
Errorfrom the template if validation fails
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ using Cloudflare Workers
{ "name": "noreply-mail-worker", "main": "src/index.ts", "compatibility_date": "2025-09-27", // Email configuration "send_email": [ { "name": "EMAIL", "allowed_destination_addresses": ["your-email@example.com"], "remote": true, }, ], // Environment variables "vars": { "RECAPTCHA_ENABLED": "false", // Set to "true" to enable reCAPTCHA "RECAPTCHA_PROJECT_ID": "", // Your Google Cloud project ID "RECAPTCHA_API_KEY": "", // Your reCAPTCHA Enterprise API key "RECAPTCHA_SITE_KEY": "", // Your reCAPTCHA site key "TO_EMAIL": "your-email@example.com", // Default recipient email "FROM_EMAIL": "no-reply@yourdomain.com", // Sender email address "AUTHORIZED_ORIGINS": "https://yourdomain.com,https://www.yourdomain.com", "ENABLE_LOGGING": "false", // Set to "true" for debug logging }, }