How to use reCAPTCHA with Laravel and Vue

How to use reCAPTCHA with Laravel and Vue

In this guide, I will show you how to implement reCAPTCHA v3 within a Laravel + Inertia.js application.

What is it reCAPTCHA?

reCAPTCHA is a free service provided by Google that helps to protect sites from spam and abuse. It consists of a turing test to distinguish humans and bots apart, and block bots from performing actions on your site (such as submitting a form).

In this guide we will be using reCAPTCHA v3, which unlike v2 will not require your users to complete a puzzle, it instead operates behind the scenes and assigns each user a score based on how likely they are to be a bot.

Where should I use reCAPTCHA?

You can use reCAPTCHA as much as you want, anywhere you want. But generally, its best to stick to the most critial and sensitive areas of your site, for example:

  • Login / sign-up forms

  • Newsletter sign-up forms (some services like Mailchimp require you to have this behind a captcha or login page as part of their terms).

  • Password reset/change forms

  • Checkout/payment forms

Adding reCAPTCHA to your application

1. Register your site

Navigate to https://www.google.com/recaptcha/admin/create and select “reCAPTCHA v3”, you will also need to whitelist your development and production domains.

On completion, you will be presented with two keys:

You can add these to your application so we can use them later:

// In .env

GOOGLE_RECAPTCHA_SECRET_KEY=XXXXXXXXX
GOOGLE_RECAPTCHA_SITE_KEY=XXXXXXXXX
// In config/services.php

'google' => [
  'recaptcha' => [
    'secret_key' => env('GOOGLE_RECAPTCHA_SECRET_KEY'),
    'site_key' => env('GOOGLE_RECAPTCHA_SITE_KEY'),
  ],
],

2. Add script to your head

The reCAPTCHA script needs to be loaded in the head section of your site, and have the site key passed to it.

You can do this in your main blade template (this is usually app.blade.php)

<script src="https://www.google.com/recaptcha/api.js?render={{ config('services.google.recaptcha.site_key') }}"></script>

3. Add a captcha to your form (client-side)

We need to generate a client-side token and attach it to the form. Below is an example of a newsletter sign-up form:

<template>
  <form @submit.prevent="submit">
    <h3>Sign-up to our newsletter</h3>
    <label>
      Email address
      <input v-model="form.email" type="email" name="email" />
    </label>
  </form>
</template>

<script>
export default {
  data () {
    return {
      // Create a standard inertia form and add a captcha_token property
      form: this.$inertia.form({
        email: '',
        captcha_token: '',
      })
    }
  },
  methods: {
    submit () {
      window.grecaptcha
        .execute(this.$page.props.recaptcha_key, { action: 'submit' })
        .then((token) => {
           // Attach the token to the form
           this.form.captcha_token = token
           
           // Submit the form
            this.form.post('/newsletter', { ... })
        });
    }
  }
}
</script>

You also need to share the ReCaptcha key with your component, in the above example you can see we are using this.$page.props.recaptcha_key. This can be configured in your HandleInertiaRequests middleware:

/**
 * Defines the props that are shared by default.
 *
 * @see https://inertiajs.com/shared-data
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
public function share(Request $request): array
{
    return array_merge(parent::share($request), [
        'recaptcha_key' => config('services.google.recatpcha.site_key'),
    ]);
}

4. Validate the reCAPTCHA token (server-side)

When the form has been submitted with the attached token we need to verify it using the API, to do this we can create a simple validation rule:

<?php

namespace App\Rules;

use Closure;
use Illuminate\Support\Facades\Http;
use Illuminate\Contracts\Validation\ValidationRule;

class Recaptcha implements ValidationRule
{
    /**
     * Run the validation rule.
     *
     * @param  \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
            'secret' => config('services.google.recaptcha.secret_key'),
            'response' => $value,
        ]);

        if ($response->successful() && $response->json('success') && $response->json('score') > 0.5) {
            return;
        }

        $fail('Recaptcha validation failed.');
    }
}

This can then be used in your validation rules:

'captcha_token' => [
    'required',
    new Recaptcha(),
],

Inertia.jsVueSecurityValidation

Published March 26th, 2025 by Jacob Fitzpatrick

Subscribe to newsletter

Subscribe to our newsletter

Receive news about new and exciting packages.

Our newsletter isn't quite ready just yet, enter your email above to be the first in line.

© 2025 Laragist

Laragist is an unofficial site and not affiliated with Laravel Holdings Inc

By @JacobFitzp and @carolinepixels