How to Integrate Google reCAPTCHA v3 in a Laravel Project
Learn how to integrate Google reCAPTCHA v3 in Laravel project for spam protection. Step-by-step guide with custom validation, and form integration.

Step 1: Create Google reCAPTCHA Credentials (Site Key & Secret Key)
Step 2: Set Up a Laravel Project
First, create a new Laravel project using the following command:
laravel new google-v3-reCAPTCHA
This will generate a fresh Laravel installation in a directory named google-v3-reCAPTCHA.
Step 3: Configure .env
Open the .env file and update the database configuration with your database credentials:
RECAPTCHA_URL=https://www.google.com/recaptcha/api/siteverify
V3_RECAPTCHA_SITE_KEY=**************************
V3_RECAPTCHA_SECRET_KEY=**************************
Step 4: Update config/services.php
Add the reCAPTCHA configuration to the config/services.php file:
'recaptcha' => [
'url' => env('RECAPTCHA_URL'),
'v3-recaptcha-site-key' => env('V3_RECAPTCHA_SITE_KEY'),
'v3-recaptcha-secret-key' => env('V3_RECAPTCHA_SECRET_KEY'),
],
Step 5: Create a Migration File
Generate a migration file for the contacts table:
php artisan make:migration create_contacts_table
Update the migration file to define the table schema. For example:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('contacts', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('contacts');
}
};
Run the migration to create the table:
php artisan migrate
Step 6: Create a Model
Generate a model for the Contact table:
php artisan make:model Contact
Update the model file if necessary, such as defining fillable fields:
App/Models/Contact.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Contact extends Model
{
protected $guarded = [];
}
Step 7: Update the Routes
Define routes for the contact form in routes/web.php:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ContactController;
Route::get('/', function () {
return view('welcome');
});
Route::get('contact', [ContactController::class, 'index'])->name('contactPage');
Route::post('contact', [ContactController::class, 'submit'])->name('contact');
Step 8: Create a Controller
Generate a controller for handling contact form submissions:
php artisan make:controller ContactController
Update the controller to include methods for displaying the form and processing submissions. For example:
App/Http/Controllers/ContactController.php
<?php
namespace App\Http\Controllers;
use App\Models\Contact;
use App\Http\Requests\ContactRequest;
class ContactController extends Controller
{
public function index()
{
return view('contact');
}
public function submit(ContactRequest $request)
{
$data = $request->validated();
unset($data['recaptcha']);
Contact::create($data);
return redirect(route('contactPage'))->with('success', 'Contact details save successfully');
}
}
Step 9: Create a Form Request File
Generate a form request for validation:
php artisan make:request ContactRequest
Update the App/Http/Requests/ContactRequest.php file to include validation rules:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ContactRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
$rules = [
'name' => ['required', 'string'],
'email' => ['required', 'string'],
'recaptcha' => ['required', 'V3captcha'],
];
return $rules;
}
public function messages()
{
return [
'recaptcha' => 'Invalid Captcha Please try again',
];
}
}
Step 10: Create a Custom Validator
Create a custom validator for reCAPTCHA v3. Save the file at app/Validators/V3Captcha.php:
<?php
namespace App\Validators;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
class V3Captcha
{
/**
* @throws GuzzleException
*/
public function validate($attribute, $value, $parameters, $validator): bool
{
$client = new Client;
$remoteIp = $_SERVER['REMOTE_ADDR'];
$response = $client->post(
config('services.recaptcha.url'),
[
'form_params' => [
'secret' => config('services.recaptcha.v3-recaptcha-secret-key'),
'response' => $value,
'remoteip' => $remoteIp,
],
]
);
$body = json_decode((string) $response->getBody());
if (! $body->success) {
return false;
}
if ($body->score >= 0.5) {
return true;
} else {
return false;
}
}
}
Register the custom validator in the App\Providers\AppServiceProvider:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Validator::extend('V3captcha', 'App\\Validators\\V3Captcha@validate');
}
}
Step 11: Add reCAPTCHA to the contact Form
Include the reCAPTCHA script and a hidden input field in your form:
resources/views/contact.blade.php:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Us</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header bg-primary text-white text-center">
<h4>Contact Us</h4>
</div>
<div class="card-body">
@if (session('success'))
<div class="text-success">{{session('success')}}</div>
@endif
<form action="{{route('contact')}}" method="post">
@csrf
<div class="mb-3">
<input type="hidden" name="recaptcha" id="recaptcha">
@error('recaptcha')
<div class="text-danger">{{$message}}</div>
@enderror
</div>
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" name="name" class="form-control" id="name" value="{{old('name') ?? ''}}" placeholder="Enter your name" />
@error('name')
<div class="text-danger">{{$message}}</div>
@enderror
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" class="form-control" id="email" value="{{old('email') ?? ''}}" placeholder="Enter your email" />
@error('email')
<div class="text-danger">{{$message}}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary w-100">Submit</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js?render={{ config('services.recaptcha.v3-recaptcha-site-key') }}"></script>
<script>
grecaptcha.ready(function() {
grecaptcha.execute("{{ config('services.recaptcha.v3-recaptcha-site-key') }}", {action: 'submit'}).then(function(token) {
if (token) {
document.getElementById('recaptcha').value = token;
}
});
});
</script>
</body>
</html>
Step 13: Run the Project
Start the Laravel development server:
php artisan serve
Note: Before testing, ensure that your project’s domain is added to the Google reCAPTCHA admin console.
Output:
Conclusion
By following these steps, you’ve successfully integrated Google reCAPTCHA v3 into your Laravel project. This will help protect your forms from spam and abuse while providing a seamless user experience. If you have any questions or run into issues, feel free to leave a comment below!
Happy coding! 🚀
Tags
Do you Like?