Skip to content

Instantly share code, notes, and snippets.

@Nooshu
Created March 9, 2026 19:49
Show Gist options
  • Select an option

  • Save Nooshu/0e1cb510f533e8ae9247f84bf9c724b3 to your computer and use it in GitHub Desktop.

Select an option

Save Nooshu/0e1cb510f533e8ae9247f84bf9c724b3 to your computer and use it in GitHub Desktop.
This is the code that runs on my Cloudflare worker to allow sending of emails from my website contact form to my choosen email address.
import { escapeHtml } from '../../_helpers/escape-html.js';
export const onRequestPost = async ({ request, env }) => {
try {
// Accept standard form submissions
const contentType = request.headers.get('content-type') || '';
if (!contentType.includes('application/x-www-form-urlencoded') && !contentType.includes('multipart/form-data')) {
return new Response(JSON.stringify({ error: 'Unsupported content type' }), {
status: 415,
headers: { 'content-type': 'application/json' },
});
}
const form = await request.formData();
// Honeypot - reject if set
if (form.get('_akjhaskjdkjhakjshdadjknjnkdsa')) {
const hpUrl = new URL('/', request.url);
return Response.redirect(hpUrl.toString(), 303);
}
const name = (form.get('name') || '').toString().trim();
const email = (form.get('email') || '').toString().trim();
const message = (form.get('message') || '').toString().trim();
const redirectUrl = (form.get('_redirect') || '/contact/thanks/').toString();
if (!name || !email || !message) {
return new Response(JSON.stringify({ error: 'Missing required fields' }), {
status: 400,
headers: { 'content-type': 'application/json' },
});
}
// Basic validation
if (name.length > 200 || email.length > 320 || message.length > 5000) {
return new Response(JSON.stringify({ error: 'Invalid field lengths' }), {
status: 400,
headers: { 'content-type': 'application/json' },
});
}
// Verify reCAPTCHA v3 token - REQUIRED for all submissions
const recaptchaToken = form.get('g-recaptcha-response') || form.get('h-captcha-response');
if (!recaptchaToken) {
return new Response(JSON.stringify({ error: 'reCAPTCHA token is required' }), {
status: 400,
headers: { 'content-type': 'application/json' },
});
}
const secret = env.RECAPTCHA_SECRET_KEY || env.RECAPTCHA_SECRET;
if (!secret) {
return new Response(
JSON.stringify({
error: 'Server not configured for CAPTCHA (missing RECAPTCHA_SECRET_KEY/RECAPTCHA_SECRET)',
}),
{ status: 500, headers: { 'content-type': 'application/json' } }
);
}
// Include remote IP for additional verification (best practice)
const remoteIp = request.headers.get('CF-Connecting-IP') || request.cf?.clientAddress || null;
const verifyParams = new URLSearchParams({
secret,
response: recaptchaToken.toString(),
});
if (remoteIp) {
verifyParams.append('remoteip', remoteIp);
}
const verifyResp = await fetch('https://www.google.com/recaptcha/api/siteverify', {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: verifyParams,
});
if (!verifyResp.ok) {
return new Response(JSON.stringify({ error: 'Failed to verify reCAPTCHA' }), {
status: 502,
headers: { 'content-type': 'application/json' },
});
}
const verifyJson = await verifyResp.json();
// Check if verification was successful
if (!verifyJson.success) {
// Log error codes for debugging (in production, you might want to log this server-side)
const errorCodes = verifyJson['error-codes'] || [];
return new Response(
JSON.stringify({
error: 'CAPTCHA verification failed',
details: errorCodes.length > 0 ? errorCodes.join(', ') : 'Unknown error',
}),
{ status: 400, headers: { 'content-type': 'application/json' } }
);
}
// Verify score (reCAPTCHA v3 returns scores between 0.0 and 1.0)
// Lower scores indicate bot-like behavior. 0.5 is a common threshold, but 0.3 allows more legitimate traffic
if (typeof verifyJson.score === 'number' && verifyJson.score < 0.3) {
return new Response(
JSON.stringify({
error: 'CAPTCHA verification failed',
details: `Score too low: ${verifyJson.score}`,
}),
{ status: 400, headers: { 'content-type': 'application/json' } }
);
}
// Optional: Verify hostname matches (helps prevent token reuse from other domains)
const requestHost = new URL(request.url).hostname;
if (
verifyJson.hostname &&
verifyJson.hostname !== requestHost &&
!requestHost.endsWith(`.${verifyJson.hostname}`)
) {
// Allow subdomains but log mismatch
console.warn(`reCAPTCHA hostname mismatch: expected ${requestHost}, got ${verifyJson.hostname}`);
}
// Prepare email via Resend
const fromEmail = 'website@nooshu.com';
const subject = form.get('_email.subject')?.toString() || 'New Message from Nooshu.com';
const textBody = `New contact form submission on nooshu.com\n\nName: ${name}\nEmail: ${email}\n\nMessage:\n${message}`;
const htmlBody = `<h2>New contact form submission on nooshu.com</h2><p><strong>Name:</strong> ${escapeHtml(name)}<br/><strong>Email:</strong> ${escapeHtml(email)}</p><p><strong>Message:</strong></p><pre style="white-space:pre-wrap">${escapeHtml(message)}</pre>`;
const apiKey = env.RESEND_API_KEY;
if (!apiKey) {
return new Response(
JSON.stringify({
error: 'Server not configured for email (missing RESEND_API_KEY)',
}),
{ status: 500, headers: { 'content-type': 'application/json' } }
);
}
const resendResp = await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: {
'content-type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
from: `Nooshu Contact <${fromEmail}>`,
to: ['website@nooshu.com'],
reply_to: email,
subject,
text: textBody,
html: htmlBody,
}),
});
if (!resendResp.ok) {
const text = await resendResp.text();
return new Response(JSON.stringify({ error: 'Email send failed', details: text }), {
status: 502,
headers: { 'content-type': 'application/json' },
});
}
// Redirect to thank you page (absolute URL required)
const finalRedirect = new URL(redirectUrl, request.url);
return Response.redirect(finalRedirect.toString(), 303);
} catch (err) {
return new Response(JSON.stringify({ error: 'Server error', details: String(err) }), {
status: 500,
headers: { 'content-type': 'application/json' },
});
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment