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

Recaptcha Bundle Laravel Package

pwiejak/recaptcha-bundle

pwiejak/recaptcha-bundle integrates Google reCAPTCHA into Symfony applications, helping protect forms and endpoints from spam and automated abuse. It offers straightforward configuration, server-side verification, and simple service integration for adding reCAPTCHA checks to your project.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before you install anything: this is a Symfony bundle (a fork of ewz/recaptcha-bundle), not a Laravel package. There is no service provider, no facade, no config/recaptcha.php, and it will not autoload into a Laravel app. As a Laravel developer your actual day-to-day leverage is one of three things:

  1. You also maintain a Symfony app and want one reCAPTCHA integration across both codebases.
  2. You're reading it as a reference implementation before writing your own Laravel validator rule.
  3. You're evaluating it for a Symfony service you've inherited.

If you only write Laravel, skip to "Implementation Patterns" for the porting notes and use biscolab/laravel-recaptcha instead.

Minimal Symfony setup (what the package actually expects):

composer require pwiejak/recaptcha-bundle

Then, in order:

  1. Find the real namespace first. Forks frequently rename the vendor namespace but leave the bundle class name. Grep before you guess:

    grep -r "class .*Bundle" vendor/pwiejak/recaptcha-bundle/src/
    

    Register whatever that returns in config/bundles.php:

    return [
        // ...
        Pwiejak\RecaptchaBundle\PwiejakRecaptchaBundle::class => ['all' => true],
    ];
    
  2. Find the config root key. Upstream EWZ uses ewz_recaptcha:; a fork may keep that key verbatim (common, to avoid breaking copied configs) or rename it. Check src/DependencyInjection/Configuration.php — the ->rootNode() / tree builder name is authoritative.

  3. Configure with env vars, never literals:

    # config/packages/recaptcha.yaml
    ewz_recaptcha:            # verify this key against Configuration.php
        public_key:  '%env(RECAPTCHA_PUBLIC_KEY)%'
        private_key: '%env(RECAPTCHA_PRIVATE_KEY)%'
        enabled: true
        locale: '%kernel.default_locale%'
    
  4. First use case — a contact form checkbox:

    $builder->add('recaptcha', RecaptchaType::class);
    

    The bundle supplies a form type (renders the widget + JS) and a constraint (IsTrue upstream) that calls Google's siteverify endpoint server-side during form validation.

If you're in Laravel and need this working today, wire it manually — it's about 15 lines:

// app/Rules/Recaptcha.php
public function passes($attribute, $value): bool
{
    return Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
        'secret'   => config('services.recaptcha.secret'),
        'response' => $value,
        'remoteip' => request()->ip(),
    ])->json('success', false);
}

That is genuinely the whole feature. The bundle's value is the form-type/twig plumbing around it, which Laravel doesn't share.

Implementation Patterns

Symfony-side patterns (what the bundle is built for):

  • Attach to every public form, not just login. The idiomatic placement is a reusable form type or a form extension so you don't hand-add the field to 12 forms. Upstream EWZ supports an enabled flag, so gate it per-environment:

    # config/packages/test/recaptcha.yaml
    ewz_recaptcha:
        enabled: false
    

    This is the single most useful pattern — tests and local dev skip the network call entirely.

  • Invisible reCAPTCHA is configured by a flag on the field/type rather than a different type. You bind it to the submit button. Verify the exact option name in the fork's RecaptchaType::configureOptions(); upstream uses as_invisible / attr keys, but forks do drift here.

  • Validation lives in the constraint, not the controller. You don't call siteverify yourself; you $form->isValid() and the constraint handles it. This is the Symfony analogue of a Laravel FormRequest rule — same mental model: validation is declarative and colocated with the form.

  • Bridging into a mixed stack. If you have Laravel fronting a Symfony admin app, keep reCAPTCHA isolated to the Symfony side and let Laravel verify its own forms independently. Don't try to share one widget across a Blade view and a Twig view; you'll get duplicate grecaptcha script includes and unpredictable rendering.

  • Laravel ↔ bundle concept map when porting:

    Symfony bundle Laravel equivalent
    Bundle registration (bundles.php) Auto-discovery via composer.json extra.laravel
    config/packages/*.yaml config/services.php
    RecaptchaType form field Blade component / {!! recaptcha_field() !!}
    IsTrue validator constraint Custom validation Rule
    enabled: false in test config Recaptcha::shouldReceive(...) or Http::fake()
  • Testing pattern. In Symfony functional tests, disable via config (above) and assert the field exists rather than asserting Google's response. Never let CI hit google.com/recaptcha/api/siteverify — it will rate-limit and flake.

Laravel-side patterns, if you're porting the idea:

  • Wrap the verification in a Rule and reuse it across FormRequests; fail closed (return false on network error) rather than open.
  • Http::fake() in tests so the rule is deterministic.
  • For v3 (score-based, no checkbox), the bundle's checkbox-oriented constraint is a poor fit — you'd need a score threshold check, which this fork almost certainly doesn't implement. Don't try to bend it; write the rule.

Gotchas and Tips

  • Zero stars, zero dependents is a real risk signal. This is a personal fork. Check whether it tracks upstream ewz/recaptcha-bundle commits; if it's a one-off snapshot, you're pinned to whatever Symfony version it was forked against. Prefer upstream EWZ unless the fork fixes something you specifically need, and vendor/pin the exact version.

  • Namespace and config-key drift are the #1 time sink. Forks of EWZ often keep the ewz_recaptcha config root while renaming the PHP namespace. Every doc snippet you find online for EWZ will use the old namespace. Always read Configuration.php and the src/ directory rather than trusting blog posts.

  • Secrets leak easily here. These bundles read keys from YAML, and people inline them. Always '%env(...)%', and add both keys to .env.example and your secret manager. A reCAPTCHA secret in a committed YAML is a live credential.

  • The public key must be registered for the exact hostname. localhost behaves specially — Google ships official test keys (6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI / 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe) that always pass. Use them in local/staging instead of disabling validation, so you exercise the real code path.

  • Client-side success proves nothing. The widget can be spoofed; only the server-side siteverify call matters. If form submission works with JS disabled, the constraint isn't wired up.

  • Token is single-use and expires (~2 min). Don't cache the response and don't re-validate the same token in two places (e.g., a Laravel controller and a Symfony listener). This bites people who add reCAPTCHA at both the form layer and an API gateway.

  • Asset/JS inclusion is the usual rendering gotcha. If the widget renders as an empty div, the grecaptcha script isn't being emitted — check the Twig form theme is registered and that your layout actually renders form_rest/the block. In webpack setups, confirm the bundle's JS isn't being tree-shaken away.

  • Extension points, if you do adopt it: the form type can be extended with a custom form theme block, and the constraint is a normal Symfony validator, so you can decorate/subclass it. If you need v3 or Enterprise, that's a fork of the constraint, not a config flag.

  • After any config change: bin/console cache:clear. Symfony config caching will happily serve your old broken key for an hour otherwise.

  • Final recommendation for a Laravel-only shop: don't adopt this. The integration surface you'd be paying for (form type, Twig widget, DI config) is entirely absent in Laravel, and the fork is unmaintained-looking. Use biscolab/laravel-recaptcha, or the ~15-line Http::asForm() rule above, and you'll have fewer moving parts and a package that actually autoloads.

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