Hello there,
I had previously implemented a client-side CAPTCHA solution, which I have now successfully replaced. However, I’m facing challenges with the server-side code, which was developed by a developer who has since closed down his business.
I’m seeking assistance to replace my current CAPTCHA with Cloudflare’s CAPTCHA service. While I’ve managed to load and verify the CAPTCHA successfully, I encounter a ‘405 Method Not Allowed’ error when trying to submit the form.
Below is the existing server-side code that I need help with:
// Config
const whitelist = {
example.com': '[email protected]',
'example.pages.dev': '[email protected]',
}
const blacklistedFields = ['g-recaptcha-response', 'h-captcha-response']
const templateID = 10
// End of config
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Methods': 'POST',
'Access-Control-Max-Age': '86400',
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
/**
* Handle POST requests only from whitelisted domains
* @param {Request} request
* @returns {Response}
*/
function handleRequest(request) {
// Handle CORS preflight requests
if (request.method === 'OPTIONS') return verifyOrigin(request)
// Only handle POST requests
if (request.method === 'POST') return handlePostRequest(request)
// Block all other methods
return new Response('Method Not Allowed', {
headers: corsHeaders,
status: 405
})
}
/**
* Block requests from hosts which are not in the whitelist
* @param {Request} request
* @returns {Response}
*/
function verifyOrigin(request) {
const origin = request.headers.get('Origin')
if (origin !== null) {
const url = new URL(origin)
if (!(url.hostname in whitelist))
return new Response(`${url.hostname} is blacklisted!`, {
headers: corsHeaders,
status: 403
})
return new Response(null, {
headers: corsHeaders,
status: 200,
})
}
return new Response('Invalid request!', {
headers: corsHeaders,
status: 400
})
}
/**
* Handle URL encoded POST data
* @param {Request} request
* @returns {Response}
*/
function handlePostRequest(request) {
const response = verifyOrigin(request)
if (response.status !== 200) return response
// Block requests having non-form-data body
const contentType = request.headers.get('content-type')
let errorMessage = null
if (!contentType) errorMessage = 'Content-Type is not set!'
const allowedTypes = ['multipart/form-data', 'application/x-www-form-urlencoded']
if (!(allowedTypes.some(type => contentType.includes(type))))
errorMessage = 'Data format not supported'
if (errorMessage)
return new Response(errorMessage, {
headers: corsHeaders,
status: 400
})
return parsePayload(request)
}
/**
* Parse JSON encoded payload in the POST request.
* @param {Request} request
* @returns {Response}
*/
async function parsePayload(request) {
const url = new URL(request.headers.get('Origin'))
const data = await request.formData()
const fields = []
for (const [key, value] of data.entries())
if (!blacklistedFields.includes(key))
fields.push({ name: key, value: value })
var response = await verifyCaptcha(data.get('h-captcha-response'))
const responseData = await response.json()
if (!responseData['success'])
return new Response(`hCaptcha: ${responseData['error-codes'].join(' ')}`, {
headers: corsHeaders,
status: 400
})
var body = {
to: [{ email: whitelist[url.hostname] }],
templateId: templateID,
params: { fields: fields }
}
if (data.has('email')) {
body['replyTo'] = {
email: data.get('email')
}
if (data.has('name')) {
body.replyTo['name'] = data.get('name')
}
}
response = await callAPI(body)
if (response.status === 201)
return new Response('Thank you! The form was submitted successfully.', {
headers: corsHeaders,
status: 200,
})
return new Response('The form could not be submitted.', {
headers: corsHeaders,
status: response.status
})
}
/**
* Call the API with passed `body` to send email (this is an email service used via api)
// api code here.. removed to protect api key
* Verify hCaptcha response token
* @param {String} token
* @returns {Response}
*/
async function verifyCaptcha(token) {
const body = {
response: token,
secret: '000000' //the secret key is removed to protect the key from misuse
}
const request = new Request('https://hcaptcha.com/siteverify', {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams(body).toString(),
})
const response = await fetch(request)
return response
}
I appreciate any guidance or assistance you can provide to resolve this issue.
Thank you.