Skip to content

Using reCAPTCHA on a Astro form

Here’s how I used Recaptcha on a Astro form to prevent spam and form submission abuse.

Set Recaptcha first, and grab the RECAPTCHA_SITE_KEY and RECAPTCHA_SECRET_KEY variables, put them in .env or anywhere you manage env vars.

Then in the Astro component:

<script
  is:inline
  src='https://www.google.com/recaptcha/api.js'></script>
<script is:inline>
  function recaptcha() {
    document.querySelector('form').submit()
  }
</script>

<form method='post'>
 ...
  <input
    type='submit'
    class='block w-full px-3 py-2 mt-8 text-sm font-semibold text-center text-white cursor-pointer leading-6 rounded-md g-recaptcha focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 ring-1 ring-inset ring-blue-200 hover:ring-blue-300 hover:bg-blue-600'
    data-sitekey={import.meta.env
      .RECAPTCHA_SITE_KEY ||
      process.env.RECAPTCHA_SITE_KEY}
    data-callback='recaptcha'
    data-action='submit'
    value='Login'
  />
</form>

Server-side form POST request handler:

export async function processCaptcha(g_recaptcha_response: string) {
  const url =
    'https://www.google.com/recaptcha/api/siteverify'

  const requestBody = new URLSearchParams({
    secret:
      import.meta.env.RECAPTCHA_SECRET_KEY ||
      process.env.RECAPTCHA_SECRET_KEY,
    response: g_recaptcha_response
  })

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: requestBody.toString()
  })

  const data = await response.json()

  console.log(data)
  /*
  {
    success: true,
    challenge_ts: '2024-01-20T18:18:12Z',
    hostname: 'localhost',
    score: 0.9,
    action: 'submit'
  }
  */

  return data.success
}

if (Astro.request.method === 'POST') {
  const formData = await Astro.request.formData()

  const email = formData.get('email')?.toString() || ''
  const password =
    formData.get('password')?.toString() || ''
  const g_recaptcha_response =
    formData.get('g-recaptcha-response')?.toString() || ''

  const is_valid_captcha = await processCaptcha(g_recaptcha_response)

  if (!is_valid_captcha) {
    error = 'Invalid captcha'
  } else {
		//valid
  }
}

I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter

JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025

Bootcamp 2025

Join the waiting list