Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Hcaptcha Laravel Package

rvxlab/hcaptcha

Server-side hCaptcha verification for Laravel. Validate hCaptcha tokens in forms and APIs with a simple validation rule, configurable keys, and clear error messages. Lightweight, framework-native, and easy to drop into existing validation flows.

Deep Wiki
Context7

Getting Started

rvxlab/hcaptcha is a thin server-side wrapper around hCaptcha's siteverify endpoint for Laravel 8+. It gives you config, a validation rule, and (depending on version) a Blade helper for rendering the widget. Start by confirming it installs against your framework version — the package is archived and its constraints may not allow Laravel 11/12.

Minimal setup:

composer require rvxlab/hcaptcha
php artisan vendor:publish --tag=hcaptcha-config

Add credentials to .env (names may differ slightly by release; check config/hcaptcha.php after publishing):

HCAPTCHA_SITEKEY=your-site-key
HCAPTCHA_SECRET=your-secret-key

Render the widget in your form layout. hCaptcha's script is loaded from their CDN, not the package:

<script src="https://js.hcaptcha.com/1/api.js" async defer></script>

<form method="POST" action="/contact">
    @csrf
    <div class="h-captcha" data-sitekey="{{ config('hcaptcha.sitekey') }}"></div>
    <button type="submit">Send</button>
</form>

Validate on the server. The response field posted by the widget is h-captcha-response, and most hCaptcha packages accept the legacy g-recaptcha-response too:

$request->validate([
    'h-captcha-response' => ['required', 'hcaptcha'],
]);

Where to look first: config/hcaptcha.php for key names and bypass switches, then the package's src/ for the rule class and any Blade directive/component. The first real use case is almost always a public contact, registration, or password-reset form.

Implementation Patterns

Form Requests are the cleanest integration. Keep captcha validation next to the rest of the form rules:

class ContactRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name'    => ['required', 'string', 'max:120'],
            'email'   => ['required', 'email'],
            'message' => ['required', 'string', 'max:5000'],
            'h-captcha-response' => ['required', 'hcaptcha'],
        ];
    }

    public function messages(): array
    {
        return [
            'h-captcha-response.required' => 'Please complete the captcha.',
            'h-captcha-response.hcaptcha' => 'Captcha verification failed. Please try again.',
        ];
    }
}

Normalize the token field once. If you support both hCaptcha and reCAPTCHA markup, merge the token into a predictable key in prepareForValidation():

protected function prepareForValidation(): void
{
    $this->merge([
        'captcha' => $this->input('h-captcha-response')
            ?? $this->input('g-recaptcha-response'),
    ]);
}

Then validate 'captcha' => ['required', 'hcaptcha'].

Inertia / SPA / JSON APIs. The browser widget still populates a hidden input, but you can also read the token explicitly and send it in your payload:

const token = document.querySelector('[name="h-captcha-response"]')?.value;

await axios.post('/api/contact', { ...form, 'h-captcha-response': token });

On the server, validate it exactly as above. Tokens are single-use and short-lived, so do not cache or replay them. If your SPA renders multiple forms, reset the widget after a failed submit (hcaptcha.reset(widgetId)).

Centralize for many endpoints. Rather than copy the rule into every controller, wrap it in a dedicated rule object or a small middleware for abuse-prone public routes. A middleware is convenient for endpoints that are not traditional form posts, but remember to return a proper 422/400 rather than a redirect for JSON clients.

Combine with rate limiting. Captcha is not a substitute for throttling. Pair it with throttle: middleware on login and registration, and keep Laravel's built-in rate limiter as the first line of defense.

Configuration-driven bypass. For local development and CI, the package usually exposes a way to disable verification (commonly a config flag). Wire it to an env variable so tests never hit hCaptcha:

HCAPTCHA_ENABLED=true
// tests/TestCase.php or phpunit.xml
<env name="HCAPTCHA_ENABLED" value="false"/>

If no bypass flag exists, bind a fake rule or mock the HTTP client in tests instead of making real network calls.

Custom verification. When you need the response payload (hostname, challenge timestamp, error codes), skip the rule and call the package's verifier/service directly, or Http::asForm()->post('https://hcaptcha.com/siteverify', [...]) yourself. This is the cleanest path for logging suspicious attempts or returning granular errors.

Gotchas and Tips

  • The package is archived. Treat it as frozen: no security patches, no Laravel 11/12 compatibility work, and no fixes for changes to hCaptcha's API. Pin the version, audit it, and plan a migration to a maintained alternative (or a 20-line custom rule) before your next major Laravel upgrade.
  • Sitekey vs secret. HCAPTCHA_SITEKEY is public and belongs in the rendered HTML; HCAPTCHA_SECRET must never leave the server. A common failure is swapping them, which produces "invalid-input-secret" from the verify endpoint.
  • The token field name matters. hCaptcha posts h-captcha-response. If your validation rule looks for a different key, validation fails even though the user solved the challenge. Dump $request->all() when debugging.
  • Tokens are single-use and expire quickly (roughly two minutes). A double-submit, a back-button resubmit, or client-side retry with the same token will fail. Reset the widget after errors.
  • Domain/hostname mismatches cause verification failures in staging. hCaptcha checks the hostname associated with the token; ensure your sitekey's allowed domains include local and staging hosts.
  • Config caching. After changing .env or config/hcaptcha.php, run php artisan config:clear (or config:cache in deploy). Stale config is a frequent cause of "it works locally but not in production."
  • Network and TLS. Verification is a server-to-server call. Behind restrictive egress firewalls, or with an outdated CA bundle, the request fails and the rule reports a generic captcha error. Log the underlying exception; the package may swallow it into a validation message.
  • Remote IP is optional but useful. Some versions forward the client IP to siteverify. Behind a load balancer, configure trusted proxies so you don't send the balancer's IP or fail hostname checks.
  • Tests should never call hCaptcha. Use the bypass flag, Http::fake(), or a fake rule. A test suite that hits the live endpoint is flaky, slow, and rate-limited.
  • Multiple widgets per page need explicit render calls and unique IDs; the automatic h-captcha class scan only handles the simple case. Load api.js once per page.
  • CSP. If you enforce Content-Security-Policy, allow https://js.hcaptcha.com and the associated frame/style origins, or the widget silently fails to render.
  • Accessibility and UX. Provide a visible error state and keep the submit button enabled only after the widget resolves; otherwise users submit an empty token and get an opaque validation failure.
  • Enterprise features are out of scope. Score-based risk assessment, custom challenges, and advanced reporting are hCaptcha account features, not something this package surfaces. If you need them, call the API directly.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource