How to implement reCaptcha in laravel?

By Shubham G on Apr 21, 2025

Learn how to integrate Google reCAPTCHA into your Laravel application to enhance security and prevent spam. This guide covers installation, validation, and best practices for implementing reCAPTCHA in Laravel forms.

Preview : 


We'll set up reCAPTCHA without relying on any packages or third-party integrations, Let's jump straight into configuring it and obtaining the necessary keys.

Step 1 : Get reCaptcha Keys

  • Goto recaptcha site and login with gmail account : https://cloud.google.com/security/products/recaptcha/
  • Once you log in, click on 'Get Started' and register new site for reCAPTCHA
  • Add the required details and submit it.
  • Once submit and approved, You will get secret key and site key, Make sure you copy it and save it somewhere.

Step 2 : Create Controller & Add Route

Once you have the keys, you'll need to create a controller and define a function within it to handle the verification process. To generate the controller, run the following command: 

php artisan make:controller RecaptchaController

Open 'web.php' and define the reCAPTCHA verification route, prefixed with 'ajax'.

use App\Http\Controllers\RecaptchaController;

Route::post('ajax/verify-recaptcha', [RecaptchaController::class, 'verifyReCaptcha'])->name('verify-recaptcha');

Head to 'RecaptchaController' and create a function name 'verifyReCaptcha'

<?php

namespace App\Http\Controllers;

use App\Rules\Recaptcha;
use Illuminate\Http\Request;

class RecaptchaController extends Controller
{
    public function verifyReCaptcha(Request $request)
    {
        $validator = \Validator::make(
            $request->all(), [
                'g-recaptcha-response' => ['required', new Recaptcha()]
            ]
        );
        if($validator->fails())
        {
            $messages = $validator->getMessageBag();
            return response()->json([
                'status' => 'error',
                'message' => __('ReCaptcha is required'),
            ]);
        }
        return response()->json([
            'status' => 'success',
            'message' => __('ReCaptcha Verified'),
        ]);
    }
}

Before implementing a rule to verify reCAPTCHA with the Google API, open the 'VerifyCsrfToken' middleware and add the required code to exclude CSRF verification for routes using the 'ajax' prefix.

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    protected $except = [
        '*/ajax/*'
    ];
}

Step 3 : Make Rule

Run the following command to generate a rule file named 'Recaptcha', which will be located in 'app/Rules/Recaptcha.php'.

php artisan make:rule Recaptcha

Open 'app/Rules/Recaptcha.php' and insert the following code, This makes an API call to verify the reCAPTCHA site and returns a response accordingly.

<?php

namespace App\Rules;

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

class Recaptcha implements ValidationRule
{
    /**
     * Run the validation rule.
     *
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        //
    }

    public function passes($attribute, $value)
    {
        $data = array('secret' => env('RECAPTCHA_SECRET_KEY'),
            'response' => $value);

        try
        {
            $response = Http::asForm()->post("https://www.google.com/recaptcha/api/siteverify", $data);

            return $response->json()['success'] ?? false;
        }
        catch (\Exception $e)
        {
            return false;
        }
    }

    public function message()
    {
        return __('ReCaptcha verification failed.');
    }
}

Note: I forgot to mention that the reCAPTCHA keys obtained from the console need to be stored somewhere in the system. I recommend placing them in the '.env' file, but you can also store them in the database or any other location based on your preference.

// .env

RECAPTCHA_SITE_KEY='6LcrM24**********************'
RECAPTCHA_SECRET_KEY='6LcrM24**********************'

Step 4 : Login Blade & JS

I'm integrating reCAPTCHA into the login process. Since we all use different themes, sharing the entire login Blade file might cause confusion. Simply add this reCAPTCHA element to your form.

<div class="row mb-3">
    <div class="col-12">
        <div class="form-group text-center">
            <div class="g-recaptcha d-inline-block" id="feedback-recaptcha" data-sitekey="{{ env('RECAPTCHA_SITE_KEY') }}"></div>
            <div class="invalid-feedback" id="recaptcha-error"></div>
        </div>
    </div>
</div>

Modify the login button type from 'submit' to 'button', and assign it the ID 'login-button'.

<button type="button" id="login-button" class="btn btn-primary">
    {{ __('Login') }}
</button>

Include the following CDNs, One for jQuery and the other for the reCAPTCHA API.

// jquery
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
// reCaptcha
<script src="https://www.google.com/recaptcha/api.js" async defer></script>

For the final step, add an 'onclick' function to the login button. This function will activate upon clicking the login button, verifying reCAPTCHA with the Google API, Using the rule we created and validated in the controller. If the verification succeeds, it will proceed with login submission, otherwise, it will display an error message.

$(document).on('click', '#login-button', function(e)
{
    e.preventDefault();
    const progressHtml = '{{ __('Processing...') }}';
    var thisForm = $(this).parents('form');

    var submit = $(this);
    submit.html(progressHtml);
    submit.prop('disabled', true);

    var enable_recaptcha = 1;
    if(enable_recaptcha > 0)
    {
        var recaptchaError = $('#recaptcha-error');
        submit.prop('disabled', true);
        $.ajax({
            url: "{{ route('verify-recaptcha') }}",
            method: 'POST',
            data: thisForm.serializeArray(),
            success: function (response)
            {
                if (response.status == 'success')
                {
                    recaptchaError.removeClass('d-block');
                    thisForm.submit();
                }
                else
                {
                    recaptchaError.addClass('d-block');
                    recaptchaError.html(response.message);
                    submit.text('{{ __('Submit') }}');
                    submit.prop('disabled', false);
                }
            },
        });
    }
    else
    {
        thisForm.submit();
    }
});
Loading...