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.

v3.1.0(1mo ago)1484↑40%MITPHPPHP ^8.2CI passing

Since May 3Pushed 1mo 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 3w ago

READMEChangelog (10)Dependencies (33)Versions (17)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.
- **Server-side verification** — your backend confirms each response with captchaapi.eu over a single call, secured by your secret key. The same model every major CAPTCHA uses, so the secret never reaches the browser.
- **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_KEY=sk_live_...
```

The site key is public and goes in the browser; the secret stays on your server. Get both 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'],
    'captchaapi_response' => ['required', 'string', new ValidCaptcha],
]);
```

Or via the string alias:

```
$request->validate([
    'email'               => ['required', 'email'],
    'captchaapi_response' => ['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 — captchaapi_response has been verified
    }
}
```

`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 `captchaapi_response` input, and dispatches to your Livewire `register`method once the response arrives.

### Showing the validation error

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

When `ValidCaptcha` rejects a response (invalid, expired, or already used), Laravel attaches the message to the `captchaapi_response` field. Render it with the included helper:

```

```

Defaults to `:for="captchaapi_response"` 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('captchaapi_response')
    {{ $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_key``CAPTCHAAPI_SECRET_KEY``null`Project secret, sent as a Bearer token on the verify call. Server-side only.`base_url``CAPTCHAAPI_BASE_URL``null`Override the API origin for the widget and the verify call. Defaults to `https://captchaapi.eu`.`timeout``CAPTCHAAPI_VERIFY_TIMEOUT``5`Seconds to wait for the verify call before treating the server as unreachable.`fail_open``CAPTCHAAPI_FAIL_OPEN``true`When the server is unreachable or returns a 5xx: `true` lets the submission through, `false` rejects it with a try-again message.`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).Fail policy
-----------

[](#fail-policy)

The verify call can fail to reach a verdict — the server is unreachable or returns a 5xx. `fail_open` decides what happens, and it defaults to **true**: the submission goes through. A CAPTCHA guards a public form, so your own outage blocking every submission is worse than the rare bot slipping past during it, and an attacker can't reach this path anyway — verification is server-to-server, off the browser.

Set it to **false** for sensitive actions (login, payment) where a missed bot costs more than a blocked visitor:

```
CAPTCHAAPI_FAIL_OPEN=false
```

The visitor is then asked to try again, never told they failed the CAPTCHA. There is no automatic retry on either setting: the response is single-use, and a second verify call would spend a token the visitor already solved.

Secret key rotation
-------------------

[](#secret-key-rotation)

Rotate from the dashboard — the package needs no list of keys. While a rotation is pending, the server accepts **both** the old and the new secret on every challenge issued, so you deploy the new key without a hard cutover:

1. In the dashboard, click **Rotate secret key** — issues a new key while the current one keeps working.
2. Update `CAPTCHAAPI_SECRET_KEY` in your `.env` and deploy. From here both keys verify, so the timing of your deploy doesn't matter.
3. In the dashboard, activate the new key, then retire the old one.

The only thing tied to the old key is a challenge a visitor was already solving when you clicked **Rotate** — it lives at most the token lifetime (~2 minutes), so any brief overlap clears itself.

For a suspected compromise, use **Revoke immediately** to drop the old key in one step. This skips the overlap, so in-flight solutions fail until the new secret is deployed.

Testing
-------

[](#testing)

In feature tests, enable fake mode so `ValidCaptcha` accepts any input without a real response or a call to the server:

```
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.

### Catching Fortify's double validation

[](#catching-fortifys-double-validation)

Fortify runs the login validator twice in one request, so a single-use response must survive both passes. To prove your integration handles this instead of silently bypassing it, opt the fake into the server's single-use contract:

```
FakeCaptchaapi::enable();
FakeCaptchaapi::enforceSingleUse();
```

The value now verifies once per request — the per-request memo covers the double call — while a replay in a later request is rejected. A missing memoization then fails the test instead of passing silently.

Laravel Octane
--------------

[](#laravel-octane)

The package is safe under Octane (Swoole, RoadRunner, FrankenPHP), where the application and its singletons stay resident across requests:

- **Verification is stateless.** Each check makes its own call to `/verify`and reads the secret from config; nothing is cached on the manager between requests.
- **The per-request memo lives on the request, not the singleton.** Octane hands each request a fresh request instance, so the memo never carries over to the next visitor — every request verifies independently.
- **Fake state is test-only.** `fake()` and `enforceSingleUse()` throw outside the `testing` environment, so the bypass and the consumed-token set can never be left set on a production worker.

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

46

—

FairBetter than 92% of packages

Maintenance91

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 96.3% 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

15

Last Release

43d ago

Major Versions

v1.0.2 → v2.0.02026-05-06

v2.1.5 → v3.0.02026-06-10

### 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 (26 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

[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M133](/packages/roots-acorn)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77922.3M186](/packages/laravel-mcp)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[propaganistas/laravel-disposable-email

Disposable email validator

6023.0M7](/packages/propaganistas-laravel-disposable-email)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M136](/packages/laravel-pulse)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)

PHPackages © 2026

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