Saturday, July 4, 2026

MEMBUAT CAPTCHA DI LARAVEL

 

Step 1 – Install the Laravel CAPTCHA package

Install the CAPTCHA package using Composer.

composer require mews/captcha

This package generates a simple image CAPTCHA and provides validation support.

Step 2 – Configure CAPTCHA settings

Publish the CAPTCHA configuration file.

php artisan vendor:publish --provider="Mews\Captcha\CaptchaServiceProvider"

This creates the captcha.php config file in the config folder.

<?php

return [
    'disable' => env('CAPTCHA_DISABLE', false),

    'characters' => [
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
    ],

    'fontsDirectory' => dirname(__DIR__) . '/vendor/mews/captcha/assets/fonts',
    'bgsDirectory' => dirname(__DIR__) . '/vendor/mews/captcha/assets/backgrounds',

    'default' => [
        'length' => 6,
        'width' => 345,
        'height' => 65,
        'quality' => 90,
        'math' => false,
        'expire' => 60,
        'encrypt' => false,
    ],

    'flat' => [
        'length' => 6,
        'fontColors' => ['#2c3e50', '#c0392b', '#16a085', '#c0392b', '#8e44ad', '#303f9f', '#f57c00', '#795548'],
        'width' => 345,
        'height' => 65,
        'math' => false,
        'quality' => 100,
        'lines' => 6,
        'bgImage' => true,
        'bgColor' => '#28faef',
        'contrast' => 0,
    ],

    'mini' => [
        'length' => 3,
        'width' => 60,
        'height' => 32,
    ],

    'inverse' => [
        'length' => 5,
        'width' => 120,
        'height' => 36,
        'quality' => 90,
        'sensitive' => true,
        'angle' => 12,
        'sharpen' => 10,
        'blur' => 2,
        'invert' => false,
        'contrast' => -5,
    ],

    'math' => [
        'length' => 9,
        'width' => 120,
        'height' => 36,
        'quality' => 90,
        'math' => true,
    ],
];

You can use the default settings without any changes.

If needed, you can customize values like:

  • length of CAPTCHA
  • image width and height
  • background and font colors

For example, to change the character length:

'length' => 5,

Most projects can use the default configuration without any changes.

Using different CAPTCHA styles

You can use different CAPTCHA styles defined in the configuration file, such as mini, inverse and more.

To use a specific style, pass its name to the captcha_img() function:

captcha_img('mini')
captcha_img('inverse')

Make sure to update the style in all places where captcha_img() is used.

In this example, it is used in:

  • the form display
  • the reload CAPTCHA method

Both should use the same style to avoid mismatch.

CAPTCHA style comparison

StyleLengthSize (approx)Special featureUse case
default6LargeStandard imageGeneral forms
flat6LargeColored text and backgroundModern UI forms
mini3SmallCompact sizeSmall layouts
inverse5MediumDistortion and contrastBetter readability
math9MediumMath-based CAPTCHAStronger bot protection

Step 3 – Create routes

use App\Http\Controllers\CaptchaController;
Route::get('/', [CaptchaController::class, 'showForm'])->name('captcha.form');
Route::post('/submit', [CaptchaController::class, 'submit'])->name('captcha.submit');
Route::get('/reload-captcha', [CaptchaController::class, 'reloadCaptcha'])->name('captcha.reload');

These routes display the form, handle form submission, and reload the CAPTCHA image.

Step 4 – Create the controller

Create a controller to display the form, validate input, and generate the CAPTCHA image.

php artisan make:controller CaptchaController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class CaptchaController extends Controller
{
    public function showForm()
    {
        return view('captcha-form', [
            'captchaImage' => captcha_img(),
        ]);
    }

    public function submit(Request $request)
    {
        $request->validate([
            'name' => ['nullable', 'string', 'max:100'],
            'email' => ['nullable', 'email'],
            'captcha' => ['required', 'captcha'],
        ], [
            'captcha.required' => 'Enter the CAPTCHA',
            'captcha.captcha' => 'CAPTCHA is incorrect',
        ]);

        return redirect()
            ->route('captcha.form')
            ->with('success', 'Form submitted successfully');
    }

    public function reloadCaptcha()
    {
        return response()->json([
            'captcha' => captcha_img(),
        ]);
    }
}

The controller displays the form, validates the CAPTCHA input, and handles the reload request.

The CAPTCHA image is generated by the mews/captcha package and displayed in the form.

On form submit, the entered value is validated using Laravel’s captcha validation rule. If it does not match, a validation error is shown.

The reload action returns a new CAPTCHA image without refreshing the page.

You can also switch CAPTCHA styles using the configuration file.

You can also switch CAPTCHA styles using the configuration file by passing the style name to the captcha_img() function.

Step 5 – Build the Blade form

Create a Blade view to display the form, CAPTCHA image, and validation messages.

resources/views/captcha-form.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Laravel CAPTCHA Form</title>
    <style>
        body {
            margin: 0;
            font-family: Arial, sans-serif;
            background: #f4f6fb;
            color: #1f2937;
        }

        .page {
            max-width: 520px;
            margin: 60px auto;
            padding: 24px;
        }

        .card {
            background: #ffffff;
            border: 1px solid #dbe3f0;
            border-radius: 12px;
            padding: 24px;
            box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08);
        }

        h1 {
            margin: 0 0 8px;
            font-size: 28px;
        }

        p {
            margin: 0 0 20px;
            color: #4b5563;
        }

        label {
            display: block;
            margin-bottom: 6px;
            font-weight: 700;
        }

        input {
            width: 100%;
            box-sizing: border-box;
            padding: 12px;
            border: 1px solid #cbd5e1;
            border-radius: 8px;
            margin-bottom: 16px;
            font-size: 15px;
        }

        .captcha-row {
            display: flex;
            align-items: center;
            gap: 12px;
            margin-bottom: 16px;
        }

        .captcha-image {
            min-height: 46px;
            padding: 6px;
            border: 1px solid #cbd5e1;
            border-radius: 8px;
            background: #fff;
        }

        button {
            border: 0;
            border-radius: 8px;
            padding: 12px 16px;
            font-size: 15px;
            cursor: pointer;
        }

        .reload-button {
            background: #e2e8f0;
            color: #1e293b;
        }

        .submit-button {
            width: 100%;
            background: #2563eb;
            color: #fff;
            font-weight: 700;
        }

        .message {
            padding: 12px 14px;
            border-radius: 8px;
            margin-bottom: 16px;
        }

        .message.success {
            background: #dcfce7;
            color: #166534;
        }

        .message.error {
            background: #fee2e2;
            color: #b91c1c;
        }

        .field-error {
            margin-top: -10px;
            margin-bottom: 16px;
            color: #b91c1c;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <div class="page">
        <div class="card">
            <h1>Simple CAPTCHA Form</h1>
            <p>Enter the CAPTCHA code shown in the image before submitting the form.</p>

            @if (session('success'))
                <div class="message success">{{ session('success') }}</div>
            @endif

            @if ($errors->has('captcha'))
                <div class="message error">{{ $errors->first('captcha') }}</div>
            @endif

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

                <label for="name">Name</label>
                <input
                    id="name"
                    type="text"
                    name="name"
                    value="{{ old('name') }}"
                    placeholder="Optional"
                >

                <label for="email">Email</label>
                <input
                    id="email"
                    type="text"
                    name="email"
                    value="{{ old('email') }}"
                    placeholder="Optional"
                >
                @error('email')
                    <div class="field-error">{{ $message }}</div>
                @enderror

                <label for="captcha">CAPTCHA</label>
                <div class="captcha-row">
                    <div class="captcha-image" id="captcha-image">{!! $captchaImage !!}</div>
                    <button class="reload-button" type="button" id="reload-captcha">Reload</button>
                </div>

                <input
                    id="captcha"
                    type="text"
                    name="captcha"
                    placeholder="Enter the CAPTCHA"
                    autocomplete="off"
                >

                <button class="submit-button" type="submit">Submit</button>
            </form>
        </div>
    </div>

    <script>
        document.getElementById('reload-captcha').addEventListener('click', async function () {
            const response = await fetch('{{ route('captcha.reload') }}', {
                headers: {
                    'X-Requested-With': 'XMLHttpRequest'
                }
            });

            const data = await response.json();
            document.getElementById('captcha-image').innerHTML = data.captcha;
            document.getElementById('captcha').value = '';
        });
    </script>
</body>
</html>

The form displays the CAPTCHA image and includes a reload option.

When the reload link is clicked, a new CAPTCHA image is loaded using a simple fetch request.

Validation errors and success messages are shown in the form after submit. 

No comments:

Post a Comment