Google ReCAPTCHA v3 for Symfony

Installs: 12 201

Dependents: 1

Suggesters: 0

Security: 0

Stars: 16

Watchers: 3

Forks: 5

Open Issues: 0


v0.1.5 2020-01-28 14:21 UTC

This package is auto-updated.

Last update: 2020-01-31 14:13:25 UTC


Build Status Scrutinizer Code Quality Code Coverage Total Downloads

reCAPTCHA v3 returns a score for each request without user friction. The score is based on interactions with your site (1.0 is very likely a good interaction, 0.0 is very likely a bot) and enables you to take an appropriate action for your site. Register reCAPTCHA v3 keys here.



With composer, require:

composer require karser/karser-recaptcha3-bundle

You can quickly configure this bundle by using symfony/flex: answer no for google/recaptcha and yes for karser/karser-recaptcha3-bundle. image

Configuration without symfony/flex:

1. Register the bundle

Symfony 4/5 Version :
Register bundle into config/bundles.php:

return [
    Karser\Recaptcha3Bundle\KarserRecaptcha3Bundle::class => ['all' => true],

Symfony 3 Version:
Register bundle into app/AppKernel.php:

public function registerBundles()
    return array(
        // ...
        new Karser\Recaptcha3Bundle\KarserRecaptcha3Bundle(),

2. Add configuration files

# config/packages/karser_recaptcha3.yaml (or app/config/config.yml if using Symfony3)
    site_key: '%env(RECAPTCHA3_KEY)%'
    secret_key: '%env(RECAPTCHA3_SECRET)%'
    score_threshold: 0.5

Add your site key and secret to your .env file:

###> karser/recaptcha3-bundle ###
###< karser/recaptcha3-bundle ###


How to integrate re-captcha in Symfony form:


use Karser\Recaptcha3Bundle\Form\Recaptcha3Type;
use Karser\Recaptcha3Bundle\Validator\Constraints\Recaptcha3;

class TaskType extends AbstractType
    public function buildForm(FormBuilderInterface $builder, array $options)
        $builder->add('captcha', Recaptcha3Type::class, [
            'constraints' => new Recaptcha3(),
            'action_name' => 'signup|resend_email|forgot_password',

How to integrate re-captcha in API method:

The idea is to require the frontend to submit the captcha token, so it will be validated on server side.

First you need to add the captcha field to your transport entity:


namespace App\Dto;

final class UserSignupRequest
    /** @var string|null */
    public $email;

    /** @var string|null */
    public $captcha;

And to add the validation constraint:

            - NotBlank: ~
            - Email: { mode: strict }
            - Karser\Recaptcha3Bundle\Validator\Constraints\Recaptcha3: ~

On frontend part you need to submit the captcha token along with email. You can obtain the captcha token either on page load or on form submit.

<script src="https://www.google.com/recaptcha/api.js?render=<siteKey>"></script>

const siteKey = '*****************-**-******-******';

//either on page load
grecaptcha.ready(function() {
    grecaptcha.execute(siteKey, {
        action: 'signup|resend_email|forgot_password'
    }).then(function(token) {
        //the token will be sent on form submit

//or on form post:
grecaptcha.ready(function() {
    grecaptcha.execute(siteKey, {
        action: 'signup|resend_email|forgot_password'
    }).then(function(token) {
        //submit the form
        return http.post(url, {email, captcha: token});

How to deal with functional and e2e testing:

Recaptcha won't allow you to test your app efficiently unless you disable it for the environment you are testing against.

# app/config/config.yml (or config/packages/karser_recaptcha3.yaml if using Symfony4)
    enabled: '%env(bool:RECAPTCHA3_ENABLED)%'
#.env.test or a stage server environment

How to add Cloudflare IP resolver:

From the Cloudflare docs: To provide the client (visitor) IP address for every request to the origin, Cloudflare adds the CF-Connecting-IP header.

"CF-Connecting-IP: A.B.C.D"

So you can implement custom IP resolver which attempts to read the CF-Connecting-IP header or fallbacks with the internal IP resolver:

<?php declare(strict_types=1);

namespace App\Service;

use Karser\Recaptcha3Bundle\Services\IpResolverInterface;
use Symfony\Component\HttpFoundation\RequestStack;

class CloudflareIpResolver implements IpResolverInterface
    /** @var IpResolverInterface */
    private $decorated;

    /** @var RequestStack */
    private $requestStack;

    public function __construct(IpResolverInterface $decorated, RequestStack $requestStack)
        $this->decorated = $decorated;
        $this->requestStack = $requestStack;

    public function resolveIp(): ?string
        return $this->doResolveIp() ?? $this->decorated->resolveIp();

    private function doResolveIp(): ?string
        $request = $this->requestStack->getCurrentRequest();
        if ($request === null) {
            return null;
        return $request->server->get('HTTP_CF_CONNECTING_IP');

Here is the service declaration. It decorates the internal resolver:

        decorates: 'karser_recaptcha3.ip_resolver'
            $decorated: '@App\Service\CloudflareIpResolver.inner'
            $requestStack: '@request_stack'


composer update