anhskohbo/no-captcha
Laravel package to integrate Google reCAPTCHA “No CAPTCHA” into your app. Provides helpers to render the JS, display normal or invisible widgets, and validate responses. Supports Laravel auto-discovery, with simple .env configuration for site key and secret.
Installation:
composer require anhskohbo/no-captcha
For Laravel ≥5.5, skip manual setup (auto-discovery enabled). For older versions, add the service provider and facade alias to config/app.php and publish the config:
php artisan vendor:publish --provider="Anhskohbo\NoCaptcha\NoCaptchaServiceProvider"
Configure .env:
NOCAPTCHA_SECRET=your_recaptcha_secret_key
NOCAPTCHA_SITEKEY=your_recaptcha_site_key
Obtain keys from Google reCAPTCHA Admin.
First Use Case: Add reCAPTCHA to a form in a Blade template:
<!-- Render JavaScript (required for reCAPTCHA) -->
{!! NoCaptcha::renderJs() !!}
<!-- Display reCAPTCHA widget -->
{!! NoCaptcha::display() !!}
<!-- Form with validation -->
<form method="POST" action="/submit">
@csrf
<!-- Other form fields -->
<button type="submit">Submit</button>
</form>
Validate in Controller:
use Anhskohbo\NoCaptcha\Facades\NoCaptcha;
use Illuminate\Support\Facades\Validator;
public function store(Request $request) {
$validator = Validator::make($request->all(), [
'g-recaptcha-response' => 'required|captcha',
// Other validation rules...
]);
if ($validator->fails()) {
return redirect()->back()->withErrors($validator);
}
// Process form...
}
{!! NoCaptcha::renderJs('en', false, 'onloadCallback') !!}
{!! NoCaptcha::display(['data-theme' => 'light']) !!}
public function rules() {
return [
'g-recaptcha-response' => 'required|captcha',
];
}
displaySubmit() for forms where UX should be seamless:
{!! NoCaptcha::displaySubmit('contact-form', 'Send Message') !!}
$response = NoCaptcha::verifyResponse($request->input('g-recaptcha-response'));
if (!$response->success()) {
// Handle failure
}
namespace App\Http\Middleware;
use Anhskohbo\NoCaptcha\Facades\NoCaptcha;
use Closure;
class VerifyRecaptcha {
public function handle($request, Closure $next) {
if ($request->isMethod('post') && !$request->has('g-recaptcha-response')) {
return response()->json(['error' => 'CAPTCHA required'], 403);
}
$response = NoCaptcha::verifyResponse($request->input('g-recaptcha-response'));
if (!$response->success()) {
return response()->json(['error' => 'Invalid CAPTCHA'], 403);
}
return $next($request);
}
}
app/Http/Kernel.php:
protected $routeMiddleware = [
'recaptcha' => \App\Http\Middleware\VerifyRecaptcha::class,
];
Route::post('/api/submit', [Controller::class, 'store'])->middleware('recaptcha');
NOCAPTCHA_ENABLED=true
@if(config('nocaptcha.enabled'))
{!! NoCaptcha::renderJs() !!}
{!! NoCaptcha::display() !!}
@endif
NoCaptcha::shouldReceive('verifyResponse')
->once()
->andReturn(new \Anhskohbo\NoCaptcha\NoCaptchaResponse(true));
g-recaptcha-response in test requests:
$response = $this->post('/submit', [
'g-recaptcha-response' => 'test-response',
'name' => 'John',
]);
<div x-data="{ showCaptcha: false }">
<button @click="showCaptcha = true">Submit</button>
@if($showCaptcha)
{!! NoCaptcha::renderJs() !!}
{!! NoCaptcha::display() !!}
@endif
</div>
public function submit(StoreRequest $request) {
return Inertia::render('Form', [
'captchaVerified' => NoCaptcha::verifyResponse($request->input('g-recaptcha-response')),
]);
}
public function store(Request $request) {
$response = NoCaptcha::verifyResponse($request->input('g-recaptcha-response'));
if (!$response->success()) {
return back()->withErrors(['captcha' => 'Invalid']);
}
// Dispatch job for further processing
}
Missing JavaScript:
NoCaptcha::renderJs() before NoCaptcha::display() causes the widget to fail silently.<head> or before the widget. For dynamic forms, ensure JS is loaded before the widget appears.Validation Timing:
g-recaptcha-response before the widget loads (e.g., in AJAX calls) may return false negatives.grecaptcha.ready(function() {
// Submit form
});
Form ID Mismatch:
displaySubmit() without matching the form ID causes the auto-submit feature to fail.displaySubmit() matches the actual form:
{!! NoCaptcha::displaySubmit('contact-form-id', 'Submit') !!}
<form id="contact-form-id" method="POST">
Environment Variables:
.env..env for NOCAPTCHA_SECRET and NOCAPTCHA_SITEKEY. Never commit .env to version control.Rate Limiting:
$response = cache()->remember("recaptcha_{$request->ip()}", now()->addMinutes(5), function () use ($request) {
return NoCaptcha::verifyResponse($request->input('g-recaptcha-response'));
});
Testing Assumptions:
NoCaptcha::verifyResponse() returns true in tests without mocking.NoCaptcha::shouldReceive('verifyResponse')
->andReturn(new \Anhskohbo\NoCaptcha\NoCaptchaResponse(true));
Widget Not Rendering:
grecaptcha undefined). Ensure NoCaptcha::renderJs() is called.NOCAPTCHA_SITEKEY is correct in .env.Validation Failing:
$response = NoCaptcha::verifyResponse($request->input('g-recaptcha-response'));
\Log::debug('reCAPTCHA Response', ['success' => $response->success(), 'error-codes' => $response->errorCodes()]);
missing-input-secret: Invalid NOCAPTCHA_SECRET.How can I help you explore Laravel packages today?