gog/recaptcha-firewall
gog/recaptcha-firewall is a Laravel middleware package that verifies Google reCAPTCHA tokens before requests reach your routes, helping block bots and spam submissions while letting legitimate users through with minimal configuration.
Before you install anything, read the source. This package has negligible public adoption (no stars, no dependents), so there is no community safety net, no third-party security review, and no Stack Overflow corpus to fall back on. Treat it as code you are adopting, not a dependency you are consuming. Budget 30–45 minutes to read src/ and composer.json before it touches a production app.
Prerequisites and first steps:
composer require gog/recaptcha-firewall
php artisan vendor:publish --provider="Gog\RecaptchaFirewall\RecaptchaFirewallServiceProvider"
Then open, in this order:
composer.json — check the illuminate/* constraints against your Laravel version, and whether extra.laravel.providers is declared (auto-discovery) or you must register the provider manually in config/app.php.config/recaptcha-firewall.php). You need to know the env key names for site key/secret, the score threshold, and any route-exemption array before you write a single line of integration code.Your first use case should be the narrowest one that proves the wiring: protect a single public form (contact or login) behind the middleware on one route, with logging enabled, and watch the output for a day before widening it.
// routes/web.php — start here, not with global middleware
Route::post('/contact', [ContactController::class, 'store'])
->middleware('recaptcha.firewall'); // confirm alias from the provider
Scope the middleware deliberately. The three tiers, in increasing risk:
auth flows (/login, /register, /password/reset), which are the highest-value targets for credential stuffing and are the reason this package exists.Exempt the machine traffic explicitly. Whatever the exemption mechanism is (config array of route names, except URIs, or a shouldSkip() check in the middleware), populate it on day one:
'exempt' => [
'stripe/webhook',
'health',
'api/*', // token-authenticated, not browser traffic
],
Client-side integration. reCAPTCHA v3 issues short-lived, single-use tokens, so the token must be minted at submit time and attached to the form payload. Illustrative shape — verify the expected input name from the middleware source, as it is often not the default g-recaptcha-response:
<form method="POST" action="/contact" id="contact-form">
@csrf
<input type="hidden" name="g-recaptcha-response" id="recaptcha-token">
...
</form>
<script src="https://www.google.com/recaptcha/api.js?render={{ config('recaptcha-firewall.site_key') }}"></script>
<script>
document.getElementById('contact-form').addEventListener('submit', function (e) {
if (this.dataset.tokenized) return;
e.preventDefault();
grecaptcha.ready(() => {
grecaptcha.execute('{{ config('recaptcha-firewall.site_key') }}', { action: 'contact' })
.then(token => {
document.getElementById('recaptcha-token').value = token;
this.dataset.tokenized = '1';
this.submit();
});
});
});
</script>
Tune the threshold from data, not from defaults. Enable whatever logging the package offers (or add your own around it) and collect a week of score distributions before you pick a cutoff. A threshold of 0.5 is a common starting point for v3, but it is wrong for many audiences — privacy-conscious users, corporate VPNs, and shared IPs score low legitimately. If the package exposes the score on the request object, log it on rejection so you can see why people were blocked.
Pair it with throttle, don't replace it. reCAPTCHA is a signal, not a rate limiter. ->middleware(['throttle:5,1', 'recaptcha.firewall']) gives you defense in depth and a cheaper first line.
Testing. You need a way to disable verification in CI and in phpunit.xml. Find it before you write tests — a config flag (RECAPTCHA_ENABLED=false) or a bindable verifier interface you can swap with a fake. If neither exists, that absence is a strong signal about the package's maturity.
composer.json (no ^ caret), and consider vendoring or forking it into your repo. Zero dependents means a silent upstream change or abandonment will not be caught by anyone but you.TrustProxies isn't configured, you are sending Google your load balancer's IP and every request looks suspicious. Fix TrustProxies before blaming the package.grecaptcha.execute(..., {action: 'contact'}) but the server verifies against 'login', verification fails even with a 0.9 score. Confirm whether the package validates action at all — many naive implementations don't, which is itself a hole worth knowing about.localhost with real keys in a useful way. Have an env-driven bypass, and make sure it is false in production by a config check rather than by convention.try/catch or add a listener so you get the underlying Google error codes.StartSession/Authenticate. If it short-circuits before ValidateCsrfToken, you have bypassed CSRF protection on that route — verify the ordering in Http/Kernel.php or bootstrap/app.php.RecaptchaVerifier-style interface bound in the container (swap it for a fake or a multi-provider fallback), a publishable config (so you can version-control settings), and a Blade directive. If none exist, fork — a ~200-line middleware is cheaper to own than to work around.How can I help you explore Laravel packages today?