PHPackages                             captchaapi/laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. captchaapi/laravel

ActiveLibrary[Security](/categories/security)

captchaapi/laravel
==================

Official Laravel SDK for captchaapi.eu — EU-hosted, GDPR-compliant proof-of-work CAPTCHA with native Livewire support.

v2.1.5(1w ago)1403↑62.5%MITPHPPHP ^8.2CI passing

Since May 3Pushed 4d agoCompare

[ Source](https://github.com/captchaapi/laravel)[ Packagist](https://packagist.org/packages/captchaapi/laravel)[ Docs](https://captchaapi.eu)[ RSS](/packages/captchaapi-laravel/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (9)Dependencies (22)Versions (13)Used By (0)

captchaapi/laravel
==================

[](#captchaapilaravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/c8e55d7f8277462f746847c846024dd30d23c6456b7b5ab0d7e8929783709dcd/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f636170746368616170692f6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/captchaapi/laravel)[![Total Downloads](https://camo.githubusercontent.com/39bb21f4b5d4ecc7127e8ad2a5f930d4c00f81d6f445def18c20e80a7122fc4c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f636170746368616170692f6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/captchaapi/laravel)[![Tests](https://camo.githubusercontent.com/ccf122aa663827d51d209bcc878415d9bdf5d71262b10eb9230a97d0af7a71ad/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f636170746368616170692f6c61726176656c2f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/captchaapi/laravel/actions/workflows/tests.yml)[![PHP Version](https://camo.githubusercontent.com/99d3ff43cc0b700ff08eaf1a5bfbccb79bc451f56d7a9a354f482b25b5b4bb61/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f636170746368616170692f6c61726176656c2f7068703f7374796c653d666c61742d737175617265)](https://packagist.org/packages/captchaapi/laravel)[![Laravel Compatibility](https://camo.githubusercontent.com/42fc7aee7537aab0acde274c31bd1de2f763d3248d19a5f4fb54699a538ac176/68747470733a2f2f62616467652e6c61726176656c2e636c6f75642f62616467652f636170746368616170692f6c61726176656c3f7374796c653d666c6174)](https://packagist.org/packages/captchaapi/laravel)[![License: MIT](https://camo.githubusercontent.com/942e017bf0672002dd32a857c95d66f28c5900ab541838c6c664442516309c8a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c61742d737175617265)](LICENSE)

Official Laravel SDK for [captchaapi.eu](https://captchaapi.eu/?utm_source=github&utm_medium=referral&utm_campaign=laravel-package) — EU-hosted, GDPR-compliant proof-of-work CAPTCHA. Drop-in Blade component, validation rule, and Livewire trait. No cookies, no tracking, no Google.

Why captchaapi.eu
-----------------

[](#why-captchaapieu)

- **EU-hosted** (Hetzner Nuremberg) — GDPR-compliant by default, no data ever leaves the EU.
- **Proof-of-work** — invisible to legitimate visitors, no friction puzzles to solve.
- **Local HMAC verification** — no server-to-server round-trip on every form submit; your backend verifies the signed attestation against your secret key in pure PHP.
- **Livewire-native** — first-class trait + Blade wrapper instead of just plain HTML form support.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 12 or 13
- (Optional) Livewire 4 for the trait + livewire-form component

Installation
------------

[](#installation)

```
composer require captchaapi/laravel
```

Publish the config file:

```
php artisan vendor:publish --tag=captchaapi-config
```

Set the credentials in `.env`:

```
CAPTCHAAPI_SITE_KEY=pk_live_...
CAPTCHAAPI_SECRET_KEYS=sk_live_...
# During rotation:
# CAPTCHAAPI_SECRET_KEYS=your_current_secret,your_pending_secret
```

Get your keys from the [project dashboard](https://captchaapi.eu/dashboard).

Usage
-----

[](#usage)

### Plain HTML form

[](#plain-html-form)

In your layout's `` (renders the widget script and pushes config to `window.CAPTCHA_*`):

```

```

In your form, add `data-captcha`:

```

    @csrf

    Send

```

In your validation:

```
use Captchaapi\Laravel\Rules\ValidCaptcha;

$request->validate([
    'email'               => ['required', 'email'],
    'captcha_attestation' => ['required', 'string', new ValidCaptcha],
]);
```

Or via the string alias:

```
$request->validate([
    'email'               => ['required', 'email'],
    'captcha_attestation' => ['required', 'captcha'],
]);
```

### Livewire

[](#livewire)

In your layout (same as above):

```

```

In your Livewire component:

```
use Captchaapi\Laravel\Concerns\WithCaptcha;
use Livewire\Component;

class RegisterForm extends Component
{
    use WithCaptcha;

    public string $email = '';

    public function register(): void
    {
        $this->validateWithCaptcha([
            'email' => 'required|email',
        ]);

        // proceed — captcha_attestation has been validated
    }
}
```

`validateWithCaptcha()` is sugar for `$this->validate(array_merge($rules, $this->rulesForCaptcha()))`. If you need to compose `validateOnly()` flows yourself, `rulesForCaptcha()` is still public.

In the component view, use the Livewire-aware form wrapper:

```

        Register

```

The wrapper sets `data-captcha-mode="event"`, includes the hidden attestation input, and dispatches to your Livewire `register` method once the attestation arrives.

### Showing the validation error

[](#showing-the-validation-error)

When `ValidCaptcha` rejects an attestation (expired, replayed, malformed signature), Laravel attaches the message to the `captcha_attestation`field. Render it with the included helper:

```

```

Defaults to `:for="captcha_attestation"` and renders a ``. Override the field name, the wrapping tag, and pass through any attributes:

```

```

The component is a thin wrapper around Laravel's `@error` directive — if you prefer to keep markup in your own templates, write it yourself:

```
@error('captcha_attestation')
    {{ $message }}
@enderror
```

Both approaches work for plain HTML forms (after a redirect-with-errors) and for Livewire (after `validateWithCaptcha()`).

Status element styling
----------------------

[](#status-element-styling)

Place a `` anywhere inside (or near) your form to show the widget's current state to your users. Six states exist (`waiting`, `idle`, `solving`, `ready`, `error`, `rate_limited`), each rendered as an icon + a localised message. The widget sets `data-captcha-state="…"` on the element so you can target each state with CSS.

The status element is **opt-in**. A form without a `[data-captcha-status]` child runs silently — submission still works, only the visible signal is absent. There is no auto-injection.

### Default colors

[](#default-colors)

The widget injects a low-specificity stylesheet on first use that applies sensible default colors per state:

StateDefault color`waiting``#6b7280` grey`idle``#6b7280` grey`solving``#6b7280` grey`ready``#059669` emerald`error``#dc2626` red`rate_limited``#d97706` amberYou don't have to write any CSS to get those colors. They appear automatically on every `[data-captcha-status]` element the widget finds.

### Override one state

[](#override-one-state)

Write a higher-specificity rule (two attribute selectors instead of one). The customer rule wins regardless of cascade order — no `!important` needed:

```
[data-captcha-status][data-captcha-state="ready"] { color: #047857; }
[data-captcha-status][data-captcha-state="error"] { color: #b91c1c; }
```

### Take over completely (Tailwind / design system)

[](#take-over-completely-tailwind--design-system)

Add `data-captcha-no-color` to suppress the widget's default stylesheet entirely. Your own classes / CSS apply cleanly:

```

```

Note that `waiting` and `ready` share the shield icon and would be visually identical without color. If you opt out of the widget defaults, supply per-state colors in your own CSS so the two stay distinguishable.

Disabling the package
---------------------

[](#disabling-the-package)

Flip `CAPTCHAAPI_ENABLED=false` in `.env` to disable both the validation rule and the widget without removing any wiring. The `ValidCaptcha` rule passes silently and `` renders nothing, so you can keep the trait, the rule, and the Blade markup in place across local, CI, and staging environments where no live site key is set.

Defaults to `true`, so existing installs keep working unchanged. This is a permanent kill-switch, not a per-test bypass — for that, use `FakeCaptchaapi::enable()` (see [Testing](#testing)).

Configuration reference
-----------------------

[](#configuration-reference)

Config keyENV variableDefaultPurpose`enabled``CAPTCHAAPI_ENABLED``true`Master kill-switch. When false, the rule passes silently and the widget renders nothing.`site_key``CAPTCHAAPI_SITE_KEY``null`Public site key from the dashboard. Required for widget rendering.`secret_keys``CAPTCHAAPI_SECRET_KEYS``[]`Comma-separated HMAC secrets. Multi-value enables zero-downtime rotation.`base_url``CAPTCHAAPI_BASE_URL``null`Override the API origin. Use only when self-hosting / proxying.`locale``CAPTCHAAPI_LOCALE``null`Force widget language (`en`, `de`, `cs`, …). Falls back to `` then `en`.`preload``CAPTCHAAPI_PRELOAD``lazy``lazy` waits for first form interaction; `eager` fires the challenge on page load.`debug``CAPTCHAAPI_DEBUG``false`Log timing breakdown in the browser console.`mode``CAPTCHAAPI_MODE``null``submit` (native form POST) or `event` (CustomEvent for Livewire/SPA).`replay_protection``CAPTCHAAPI_REPLAY_PROTECTION``true`Cache each attestation `jti` and reject duplicates within its TTL window.`cache_prefix`—`captchaapi:jti:`Prefix for cached jtis. Change only on collision with another package.`clock_skew_leeway``CAPTCHAAPI_CLOCK_SKEW_LEEWAY``60`Seconds an attestation's `iat` may sit in the future before it is rejected.Secret key rotation
-------------------

[](#secret-key-rotation)

The package accepts any matching secret in `CAPTCHAAPI_SECRET_KEYS` (a comma-separated list). Rotation has four steps:

1. In the dashboard, click **Rotate secret key** — generates a new key in the *pending* state. Your backend keeps signing with the current key.
2. Add the pending key alongside the current one in your `.env`: ```
    CAPTCHAAPI_SECRET_KEYS=current_secret,pending_secret
    ```

    Deploy.
3. In the dashboard, click **Activate pending key**. The backend now signs with the new key; your app accepts both during the handover.
4. Drop the old key on the next deploy.

For suspected-compromise scenarios, use the dashboard's **Revoke immediately** — replaces the key in one step and skips the pending phase. Briefly accepts no attestations until you deploy the new key.

Replay protection
-----------------

[](#replay-protection)

By default the validation rule caches each accepted attestation's `jti`(unique identifier in the payload) for the remainder of its TTL. A captured-in-transit attestation can therefore be submitted only once, even within its 5-minute validity window.

This requires a working cache driver (the application's default cache via `Cache::store()`). Disable in `config/captchaapi.php` if your cache is unreliable or unavailable:

```
'replay_protection' => false,
```

Use **Redis**, **Memcached**, or the **database** cache driver in production. The `file` and `array` drivers don't have an atomic `Cache::add()`, so concurrent submissions can race past the replay check.

Testing
-------

[](#testing)

In feature tests, enable fake mode so `ValidCaptcha` accepts any input without requiring you to mint real attestations:

```
use Captchaapi\Laravel\Testing\FakeCaptchaapi;

beforeEach(function () {
    FakeCaptchaapi::enable();
});

afterEach(function () {
    FakeCaptchaapi::disable();
});
```

The fake state is stored on the `Captchaapi` singleton — it does not persist across requests in production code.

Security
--------

[](#security)

If you discover a security vulnerability, please **do not** open a public GitHub issue. Use GitHub's [private vulnerability reporting](https://github.com/captchaapi/laravel/security/advisories/new)so we can coordinate a fix before details become public.

Contributing
------------

[](#contributing)

Bug reports and feature requests welcome at [github.com/captchaapi/laravel/issues](https://github.com/captchaapi/laravel/issues).

For development:

```
composer install
composer test     # Pest, parallel
composer lint     # Pint --test
composer stan     # PHPStan level 8
composer rector   # Rector --dry-run
```

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

48

—

FairBetter than 94% of packages

Maintenance99

Actively maintained with recent releases

Popularity20

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 95.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

11

Last Release

12d ago

Major Versions

v1.0.2 → v2.0.02026-05-06

### Community

Maintainers

![](https://www.gravatar.com/avatar/c2baa34cd36bf5f0ef506de6f85c7d6e6ef82508c1e3ec4ca0c86ab02dbf1ba4?d=identicon)[rajtik76](/maintainers/rajtik76)

---

Top Contributors

[![rajtik76](https://avatars.githubusercontent.com/u/12937708?v=4)](https://github.com/rajtik76 "rajtik76 (22 commits)")[![captchaapi](https://avatars.githubusercontent.com/u/281403525?v=4)](https://github.com/captchaapi "captchaapi (1 commits)")

---

Tags

bot-protectioncaptchaeugdprlaravelprivacyproof-of-workspam-protectionlaravelcaptchalivewiregdpreuspam protectionproof-of-workpowcaptchaapi

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/captchaapi-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/captchaapi-laravel/health.svg)](https://phpackages.com/packages/captchaapi-laravel)
```

###  Alternatives

[propaganistas/laravel-disposable-email

Disposable email validator

6012.9M7](/packages/propaganistas-laravel-disposable-email)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9732.3M121](/packages/roots-acorn)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

76318.2M110](/packages/laravel-mcp)[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[laravel/ai

The official AI SDK for Laravel.

9782.1M153](/packages/laravel-ai)[moonshine/moonshine

Laravel administration panel

1.3k239.9k72](/packages/moonshine-moonshine)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
