How to setup Google reCAPTCHA in Laravel with an example

by Vincy. Last modified on February 11th, 2024.

Google reCAPTCHA is a popular tool to prevent spam. It ensures the request is genuine and stops automated bot hits.

In a Laravel application implementing reCAPTCHA is an easy job. This tutorial gives steps to render a Google reCAPTCHA element to the view.

In this Laravel example, the server-side reCAPTCHA validation is enabled. A custom validation service extends the default Laravel validator. If you want to implement Google reCaptcha in PHP the link has the code.

The below list shows the steps to set up the Google ReCaptcha in the Laravel App.

  1. Get Google reCaptcha API keys and configure it to the API
  2. Add a custom Laravel rule to bind the secret key and the API endpoint.
  3. Prepare the reCaptcha validation request and process the formData on success.
  4. Build the contact form template with the Google reCaptcha element.

google recaptcha laravel

1. Prerequisites – Create Google reCaptcha keys

  1. Generate Google reCaptcha API keys using the admin console.
  2. Configure them to the Laravel application with the .env file.
  3. Configure the SMTP settings and the contact email recipient address in the .env file.

The below screenshot shows the Google reCaptcha admin console interface where the API keys are generated.

google recaptcha admin console

There are two keys public key and secret key. The linked article has the steps to get these keys. The public key is for the client to render the Google reCaptcha element to the Laravel template. The secret key is for the server-side file validation by hitting the reCaptcha API endpoint.

Configure the Laravel .env file to set the reCaptcha keys

This configurable is used to set the reCaptcha keys and the SMTP settings.

.env

GOOGLE_RECAPTCHA_PUBLIC_KEY = Your recaptcha public key
GOOGLE_RECAPTCHA_SECRET = Your recaptcha secret key

MAIL_MAILER="smtp"
MAIL_HOST=host_name
MAIL_PORT="465"
MAIL_USERNAME=Your SMTP username
MAIL_PASSWORD=Your SMTP password
MAIL_ENCRYPTION="ssl"
MAIL_FROM_ADDRESS=From email address
MAIL_FROM_NAME="${APP_NAME}"

APPLICATION_MAIL_RECIPIENT="vincy@example.com"

2. Custom validation rule for validating the Google reCaptcha

This class has the custom validation script to send the verification request to the Google reCaptcha API endpoint. It sends the API secret key along with the request.

If the response has the error status then it invokes the fail() callback to return the validation error to the UI.

app/Rules/GoogleReCaptcha.php

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log; // Import the Log facade

class GoogleReCaptcha implements ValidationRule
{
    /**
     * Run the validation rule.
     *
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $response = Http::get("https://www.google.com/recaptcha/api/siteverify", [
            'secret' => env('GOOGLE_RECAPTCHA_SECRET'),
            'response' => $value
        ]);

        if (!$response->json()["success"] ?? false) {
            $fail('Google reCaptcha validation required.');
        }
    }
}
?>

Extend Laravel validation to handle reCaptcha validation

The above rule class is instantiated at the Laravel App service provider. The validation instance calls the custom validate() function and sets the error if the user misses the reCaptcha validation.

In this provider, it names the validation hook as google_recaptcha. This name key is used in the form action handler before processing the form post request.

app/Providers/AppServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Rules\GoogleReCaptcha;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        $this->app->validator->extend('google_recaptcha', function ($attribute, $value, $parameters, $validator) {
            $rule = new GoogleReCaptcha;

            // Validate reCAPTCHA
            $rule->validate($attribute, $value, function ($message) use ($validator, $attribute) {
                $validator->errors()->add($attribute, $message);
            });
            // Return true to success
            return true;
        });
    }
}
?>

3. Send validation request before processing the formData

The below code is for having a ContactController for this Laravel example. It has two routing handler functions. One is to show the contact form with the Google reCaptcha element. And, the other is to process the post request on submitting the form.

In the form action handler processContactFormData() it executes the default Laravel required and the email format validation.

Added to the default validation, it hooks the custom reCaptcha validation rule by specifying the custom key mapped at the AppServiceProvider class.

This validation returns the validateData response. If it has no error response, the code below shows how to load the user input to the request object.

On successful Google reCaptcha validation, it processes the contact form mail-sending script in this example. It uses an SMTP mailer to send the email in Laravel.

app/Http/Controllers/ContactController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Mail\MailService;
use Illuminate\Support\Facades\Mail;

class ContactController extends Controller
{
    public function showContactForm()
    {
        return view('contact-form');
    }

    public function processContactFormData(Request $request)
    {
        $validatedData = $request->validate([
            'name' => 'required',
            'email' => 'required|email',
            'message' => 'required',
            'g-recaptcha-response' => 'google_recaptcha',
        ]);

        if (!empty($validatedData)) {
            $formData = [
                'name' => $request->input('name'),
                'email' => $request->input('email'),
                'message' => $request->input('message'),
            ];

            $recipientEmail = env('APPLICATION_MAIL_RECIPIENT');
            Mail::to($recipientEmail)->send(new MailService($formData));
        }

        return redirect()->back()->with('success', 'Hi, we received your message. Thank you!');
    }
}
?>

Web routes for the landing page and the form action URL

Two web route URL rules are created for this reCaptcha example as shown below. The /contact-us/ maps the name contact.form.action to be used in the HTML form action attribute.

routes/web.php

<?php
Route::get('/', [ContactController::class, 'showContactForm']);
Route::post('contact-us', [ContactController::class, 'processContactFormData'])->name('contact.form.action');
?>

4. Contact form template with Google reCaptcha element

The below template file is created to show a contact form in the Laravel application UI. It shows the basic nameemail, and message field with a Google reCaptcha element.

It loads the reCaptcha JavaScript API library and a target HTML container to load the "I am not a Robot" reCaptcha element.

Without attending the reCaptcha validation, this example code stops proceeding further to send the contact email.

On successful submission, the processFormData() controller function uses the Laravel Mail service to trigger the mail sending. It sets the parameter from the posted formData to the email body.

resources/views/contact-form.blade.php

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>How to setup Google reCAPTCHA in Laravel with an example</title>
    <link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.min.css" />
    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>

<body>
    <div class="container">
        <div class="row mb-5">

            <div class="col-7 offset-1 mt-5">
                <div>
                    <h3>Contact Us</h3>
                </div>
                <div class="card">

                    <div class="card-body">
                        <form method="POST" action="{{ route('contact.form.action') }}">
                            @csrf
                            <div class="col-md-6">
                                <div class="form-group">
                                    <strong>Name:</strong>
                                    <input type="text" name="name" class="form-control" value="{{ old('name') }}">
                                    @error('name')
                                    <span class="text-danger">{{ $message }}</span>
                                    @enderror
                                </div>
                            </div>
                            <div class="col-md-6">
                                <div class="form-group">
                                    <strong>Email:</strong>
                                    <input type="text" name="email" class="form-control" value="{{ old('email') }}">
                                    @error('email')
                                    <span class="text-danger">{{ $message }}</span>
                                    @enderror
                                </div>
                            </div>
                            <div class="col-md-6">
                                <div class="form-group">
                                    <strong>Message:</strong>
                                    <textarea name="message" id="message" class="form-control"
                                        rows="">{{ old('message') }}</textarea>

                                    @error('message')
                                    <span class="text-danger">{{ $message }}</span>
                                    @enderror
                                </div>
                            </div>
                            <div class="col-md-12">
                                <div class="form-group">
                                    <div class="g-recaptcha" data-sitekey="{{ env('GOOGLE_RECAPTCHA_PUBLIC_KEY') }}"></div>
                                    @error('g-recaptcha-response')
                                    <span class="text-danger">{{ $message }}</span>
                                    @enderror
                                </div>
                            </div>
                            <div class="col-md-12">
                                <div class="form-group">
                                    <button type="submit" class="btn btn-primary btn-submit">Submit</button>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>

Contact email body template

This template is for the contact mail body content. It uses the user-entered values from the contact form. Template variables are created in the Laravel routers and it is used in this email body template.

The customer_namecustomer_email, and the, customer_message variables embed the dynamic data posted on the form submit.

resources/views/emailbody.blade.php

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
    <p>Hi Administrator,</p>
    <h1>A new message is received</h1>

    @if ($customer_name)
    <p>
        Cutomer name: {{ $customer_name }}
    </p>
    @endif
    @if ($customer_email)
    <p>
        Cutomer email: {{ $customer_email }}
    </p>
    @endif
    @if ($customer_message)
    <p>
        Cutomer message: {{ $customer_message }}
    </p>
    @endif
</body>

</html>

Download

Leave a Reply

Your email address will not be published. Required fields are marked *

↑ Back to Top

Share this page