snipershady/recaptcha-bundle
A Laravel/PHP bundle for adding Google reCAPTCHA protection to forms and requests. It provides key configuration and server-side verification to help block bots and spam with minimal setup in your application.
Read this first: the package is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config/ publish tag, and nothing that php artisan will discover. For a Laravel developer the practical value is (a) if you also maintain a Symfony app, or (b) as a reference implementation for the reCAPTCHA server-side flow you'll re-implement as a Laravel validation rule. Don't composer require it into a Laravel app expecting it to do anything.
Where to look in the repo, in order:
composer.json — the symfony/framework-bundle and php constraints tell you which Symfony versions the 2025-03-02 release actually targets. This is the whole compatibility story.DependencyInjection/Configuration.php — the canonical list of config keys (public key, secret key, locale, enabled flag, version/type settings). Trust this file over the README.DependencyInjection/*Extension.php and Resources/config/* — how the form type is wired and what parameters/services it expects.Form/Type/ — the actual field class. Note its block prefix / form type alias; that string is what you reference in a Symfony form.Resources/views/ — the Twig block that emits the widget and the Google script tag.First use case (Symfony side): add a reCAPTCHA field to a contact or registration form — configure the key pair, attach the bundle's form type to the form, render it, and let form validation reject a failed server-side check.
First use case (Laravel side): nothing to install. Build the equivalent — a recaptcha validation rule that POSTs to https://www.google.com/recaptcha/api/siteverify, keys in config/services.php fed from .env, and a Blade partial for the widget.
Symfony app — the intended path
// config/packages/recaptcha.yaml (key name comes from Configuration.php)
# public_key / secret_key should be env() references, never literals
// in a form type
$builder->add('recaptcha', ReCaptchaType::class, [
'invalid_message' => 'Please confirm you are human.',
]);
.env.local in dev, real secrets in your platform's secret store). Only the public key reaches the browser.Laravel app — the pattern to port
Because the bundle's logic is essentially "render widget + POST token to siteverify + map result to a validation error", mirror that shape:
// config/services.php
'recaptcha' => [
'site_key' => env('RECAPTCHA_SITE_KEY'),
'secret_key' => env('RECAPTCHA_SECRET_KEY'),
'min_score' => env('RECAPTCHA_MIN_SCORE', 0.5), // only meaningful for v3
],
Rule object (RecaptchaRule) that calls siteverify with the token, the secret, and the client IP. Return a clear message; log the error-codes array for debugging.g-recaptcha-response input, with the site key injected from config.if statement.action and validate the returned action and score in the rule, not just success.Http::fake([...])) and assert both the pass and fail paths.Mixed-repo integration tip
If you genuinely have a Symfony service and a Laravel app in the same org, keep this bundle confined to the Symfony service. The Laravel app should not depend on it transitively — a symfony/form pull-in inside a Laravel process is dead weight and a source of conflicting psr/* constraints.
Extension point (Symfony)
The usual way to change behaviour is to decorate the form type service or override the Twig block in your app's templates, not to fork the bundle. Forking a 0-star package means you own the maintenance of every future Symfony compatibility fix.
EWZRecaptchaBundle; diff it against upstream before adopting, and pay attention to whether it carries forward the upstream security and Symfony-version fixes. Pin the exact version in composer.json and read the changelog/commit log before bumping.publishes(), no config facade. Any tutorial that says "install this in Laravel" is wrong; you want google/recaptcha or a hand-rolled rule.error-codes.example.com on localhost will render but not solve. The classic symptom is a checkbox that never completes; check the console for ERROR for site owner: Invalid domain for site key."success": false. Also verify hostname and, for v3, action and score — otherwise a token minted on another action/domain is accepted.invalid_message — override it as a field option in your form type. If you need different validation logic, decorate the service.composer why-not before upgrading Symfony, not after.How can I help you explore Laravel packages today?