Spam Protection: Implementing Google reCAPTCHA in Laravel Without a Package (With Full MVC Setup)

Spam Protection: Implementing Google reCAPTCHA in Laravel Without a Package (With Full MVC Setup)

This post walks you through implementing Google reCAPTCHA v2 in a Laravel application from scratch. We’ll create a full MVC structure to manage comments, integrate Google reCAPTCHA, and protect against spam. The focus is on providing all the necessary code and instructions for a seamless integration.


Step 1: Set Up the Laravel Application

If you don’t have a Laravel project yet, create one:

composer create-project laravel/laravel laravel-recaptcha

Navigate into the project directory:

cd laravel-recaptcha

Step 2: Create the MVC Structure for Comments

2.1. Generate the Comment Model and Migration

Run the following Artisan command to create a model and migration:

php artisan make:model Comment -m

Update the migration file database/migrations/xxxx_xx_xx_create_comments_table.php:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->string('author_name');
            $table->text('content');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('comments');
    }
};

Run the migration to create the database table:

php artisan migrate

2.2. Create the Comment Controller

Run the following command:

php artisan make:controller CommentController

Update app/Http/Controllers/CommentController.php with the following code:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use App\Models\Comment;

class CommentController extends Controller
{
    public function create()
    {
        return view('comments.create');
    }

    public function store(Request $request)
    {
        // Validate input and reCAPTCHA response
        $request->validate([
            'author_name' => 'required|string|max:255',
            'content' => 'required|string|max:1000',
            'g-recaptcha-response' => 'required|string',
        ]);

        // Verify reCAPTCHA
        $recaptchaSecret = env('RECAPTCHA_SECRET_KEY');
        $response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
            'secret' => $recaptchaSecret,
            'response' => $request->input('g-recaptcha-response'),
            'remoteip' => $request->ip(),
        ]);

        if (!$response->json('success')) {
            return redirect()->back()->withErrors(['g-recaptcha-response' => 'Captcha verification failed.'])->withInput();
        }

        // Store comment
        Comment::create([
            'author_name' => $request->author_name,
            'content' => $request->content,
        ]);

        return redirect()->route('comment.create')->with('success', 'Comment submitted successfully!');
    }
}

2.3. Define Routes

Add routes for the comment form and submission in routes/web.php:

use App\Http\Controllers\CommentController;

Route::get('/comments/create', [CommentController::class, 'create'])->name('comment.create');
Route::post('/comments', [CommentController::class, 'store'])->name('comment.store');

2.4. Update the Comment Model

Modify app/Models/Comment.php to include the necessary fields:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    use HasFactory;

    protected $fillable = ['author_name', 'content'];
}

Step 3: Add the reCAPTCHA Widget to the Form

Create a Blade view file for the comment form in resources/views/comments/create.blade.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Comment Form</title>
    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
    <h1>Submit a Comment</h1>

    @if (session('success'))
        <p style="color: green;">{{ session('success') }}</p>
    @endif

    <form method="POST" action="{{ route('comment.store') }}">
        @csrf

        <label for="author_name">Name:</label>
        <input type="text" id="author_name" name="author_name" required>
        @error('author_name')
            <p style="color: red;">{{ $message }}</p>
        @enderror

        <label for="content">Comment:</label>
        <textarea id="content" name="content" rows="5" required></textarea>
        @error('content')
            <p style="color: red;">{{ $message }}</p>
        @enderror

        <!-- Google reCAPTCHA widget -->
        <div class="g-recaptcha" data-sitekey="{{ env('RECAPTCHA_SITE_KEY') }}"></div>
        @error('g-recaptcha-response')
            <p style="color: red;">{{ $message }}</p>
        @enderror

        <button type="submit">Submit</button>
    </form>
</body>
</html>

Step 4: Configure reCAPTCHA Keys

    • Go to the Google reCAPTCHA Admin Console.

      1. Register your application by providing:
  • Label: A name for your site. ( what ever you want, you can name it : example)

  • reCAPTCHA Type: Choose either "reCAPTCHA v2" or "reCAPTCHA v3". For this tutorial, we’ll use reCAPTCHA v2 (Checkbox).

  • Domains: Specify the domains where you’ll use reCAPTCHA. ( Localhost or 127.0.0.1 )

    • Once registered, Google will provide you with:
  • A Site Key: For your frontend.

  • A Secre**t Key**: For server-side verification.

Add the following to your .``env file:

RECAPTCHA_SITE_KEY=your_site_key_here
RECAPTCHA_SECRET_KEY=your_secret_key_here

Replace your_site_key_here and your_secret_key_here with the keys from Google.


Step 5: Test the Application

  1. Run the development server:
php artisan serve
  1. Visit the comment form at http://127.0.0.1:8000/comments/create.

  2. Submit the form:

    • Without completing the reCAPTCHA: You should see an error.

    • After completing the reCAPTCHA: The comment should be saved in the database.


This completes the process. 🚀