scriptua / magento2-invisible-captcha
Magento 2 multi-provider invisible captcha & bot protection (Google reCAPTCHA v2/v3/Enterprise and Cloudflare Turnstile) for form-level and route-level protection
Package info
github.com/hryvinskyi/magento2-invisible-captcha
Type:magento2-module
pkg:composer/scriptua/magento2-invisible-captcha
Fund package maintenance!
Requires
- php: ~8.1.0||~8.2.0||~8.3.0||~8.4.0||~8.5.0
- hryvinskyi/magento2-base: ^2.1
- hryvinskyi/magento2-theme-assets: ^1.0
- magento/framework: *
- magento/module-catalog: *
- magento/module-catalog-search: *
- magento/module-checkout: *
- magento/module-config: *
- magento/module-contact: *
- magento/module-customer: *
- magento/module-graph-ql: *
- magento/module-layered-navigation: *
- magento/module-newsletter: *
- magento/module-paypal: *
- magento/module-quote: *
- magento/module-review: *
- magento/module-sales-rule: *
- magento/module-send-friend: *
- magento/module-store: *
- magento/module-webapi: *
- magento/module-wishlist: *
- nyholm/psr7: ^1.5
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.0 || ^2.0
- symfony/http-client: ^5 || ^6 || ^7 || ^8
Requires (Dev)
- roave/security-advisories: dev-latest
This package is auto-updated.
Last update: 2026-07-08 17:34:41 UTC
README
Multi-provider invisible captcha and bot protection for Magento 2.
Version 3 merges the former Hryvinskyi_TurnstileProtection module into a single,
provider-agnostic extension that protects both individual forms and whole
routes, using whichever CAPTCHA provider you configure.
Providers
| Code | Provider | Mode |
|---|---|---|
recaptcha_v2_checkbox |
Google reCAPTCHA v2 — "I'm not a robot" | visible checkbox |
recaptcha_v2_invisible |
Google reCAPTCHA v2 — invisible badge | invisible |
recaptcha_v3 |
Google reCAPTCHA v3 | invisible, score-based |
recaptcha_enterprise |
Google reCAPTCHA Enterprise | invisible, score-based (assessments API) |
turnstile |
Cloudflare Turnstile | managed / invisible |
Capabilities
- Form protection — invisibly protects the same surface as Magento's native reCAPTCHA suite: customer login (incl. AJAX popup & checkout), register, forgot password, account edit, contact, newsletter, send-to-friend, product review, share wishlist, apply coupon (cart), PayPal Payflow Pro, and the admin login / forgot-password forms. Each is toggled independently; score-based providers also support a per-form threshold.
- WebAPI / headless protection — REST and GraphQL validation for
place order, apply coupon (checkout), in-store pickup place order,
and resend confirmation email. Protected endpoints require the captcha
token in the
X-Captcha-Tokenrequest header (X-ReCaptchaalso accepted). AhryvinskyiInvisibleCaptchaConfig(formType:)GraphQL query exposes the active provider's client config for headless storefronts. Endpoint coverage is extensible viaApi\Webapi\WebapiConfigProviderInterface. - Route protection — a full-page interstitial challenge for requests that match a Cloudflare-style rule expression (field / operator / value rows joined by AND/OR). A verified visitor receives an HMAC-signed cookie and is keyed into a separate Varnish cache bucket via the HTTP context. An optional fallback provider is revealed on the challenge page after a delay. The interstitial is a self-contained, system-font page (no external font/CDN requests) whose accent color is themeable from the admin.
Installation
composer require hryvinskyi/magento2-invisible-captcha bin/magento module:enable Hryvinskyi_InvisibleCaptcha bin/magento setup:upgrade bin/magento setup:di:compile bin/magento setup:static-content:deploy
Dependencies: hryvinskyi/magento2-base, hryvinskyi/magento2-theme-assets,
symfony/http-client + PSR-7/17/18.
Configuration
Stores → Configuration → Hryvinskyi → Invisible Captcha & Bot Protection
(hryvinskyi_invisible_captcha/*):
- General — master switch, active provider, lazy-load, disable-submit, debug.
- Provider Credentials — site/secret keys (+ Enterprise project id, widget options) per provider. Secret keys are encrypted and flagged sensitive.
- Form Protection — per-form toggles and score thresholds (storefront + admin).
- Route Protection — rules editor, route-gate provider override, fallback
provider, cookie lifetime, IP / user-agent exclusions, AJAX-marker params, etc.
Includes a Challenge Page Appearance sub-group that themes the interstitial
accent palette —
primary_color(#2f6bd8),primary_color_deep(#2557b6) andprimary_color_soft(rgba(47,107,216,0.12)) underhryvinskyi_invisible_captcha/route_protection/appearance/*, injected as CSP-safe--primary*overrides and configurable per store view. - Advanced — outbound verification HTTP timeout.
Architecture (extension points)
Api\Provider\ProviderInterface+ProviderPoolInterface— add a provider by implementing the interface and adding it to theModel\Provider\PoolDI array.Api\Verification\VerifierInterface/VerificationRequestInterface/VerificationResultInterface— provider-agnostic verification.Api\Filter\FieldProviderInterface/OperatorProviderInterface— extend the route rule engine with custom fields/operators via DI arrays.Model\Strategy\{Token,Failure}\*— pluggable token extraction & failure handling.
WebAPI / checkout note
The place_order, coupon_code (checkout), store_pickup and
resend_confirmation_email keys validate server-side on the WebAPI/GraphQL
call. They are off by default. Before enabling one, make sure your storefront
/ headless client sends the captcha token in the X-Captcha-Token header on that
call (for score-based providers, execute the provider with the matching action,
e.g. place_order) — otherwise those requests will be rejected. The token for
form-level (non-WebAPI) submissions is handled automatically via the hidden
hryvinskyi_invisible_token field.
Verify endpoint & caching
The route-gate challenge POSTs to invisiblecaptcha/verify. On success it sets the
hryvinskyi_captcha_verified cookie and the CAPTCHA_VERIFIED HTTP-context vary
key. After deploying v3, run a full page cache flush (the vary key / cookie /
X-InvisibleCaptcha-Challenge header were renamed from the old Turnstile names).
Upgrading from v2.x / TurnstileProtection
Run setup:upgrade. The data patch MigrateLegacyCaptchaConfig copies your old
hryvinskyi_invisible_captcha/* (reCAPTCHA v3) and hryvinskyi_turnstile/*
settings into the new tree (encrypting the legacy plaintext v3 secret). The
standalone Hryvinskyi_TurnstileProtection module is removed — disable it after
upgrading.
Migrating from Magento's native Google reCAPTCHA
If the store already uses Magento's built-in Google reCAPTCHA (Magento_ReCaptcha*),
import its configuration into this module with:
bin/magento hryvinskyi:invisible-captcha:migrate-recaptcha --dry-run # preview the change set bin/magento hryvinskyi:invisible-captcha:migrate-recaptcha # apply
The command reads every recaptcha_frontend/* and recaptcha_backend/* row from
core_config_data (all scopes) and writes the equivalent
hryvinskyi_invisible_captcha/* settings:
- Credentials — v3, v2-checkbox and v2-invisible site/secret keys map to the matching provider under Provider Credentials. The native module stores both keys encrypted; the secret key is copied verbatim (this module's field is encrypted too, so the ciphertext stays valid under the same crypt key), while the site key is decrypted on copy because this module stores it in plain text. Run the command on the installation that owns the crypt key. Frontend keys win when frontend and backend differ.
- Per-form selectors — each
type_for/<form>that isn't "disabled" turns on the corresponding form under Form Protection (the nativeplace_ordergate enables both checkout and in-store pickup here). The v3 section-wide score threshold is fanned out to each enabled v3 form. - Derived — the active provider (most-used across the enabled forms), the master switch and the form-protection switches are set so protection stays live.
- Status hand-over — after copying, each migrated native
type_for/*selector row is cleared, so the built-in reCAPTCHA stops challenging those forms and this module takes over in the same run (source_disabledin the summary table). Native credentials are left in place, so the switch is easy to revert.
Existing values in the target tree are never overwritten unless you pass --force.
The command flushes the config cache and prints a per-path summary table.
Upgrading from a version whose migration copied the site key without decrypting it? Re-run with
--forceto overwrite the broken ciphertext values.
skipped_undecryptablerows / missing site keys? The installation's crypt key (app/etc/env.php) cannot decrypt those values — typical when the database was imported from another environment without its crypt key. Nothing else encrypted (payment keys, SMTP passwords) will decrypt on such an installation either. Restore the original crypt key (all versions, in order) or re-enter the keys in the admin, then re-run the command.
Because the hand-over disables the native reCAPTCHA at config level, physically
removing its modules is optional. If you still want them gone, the companion
metapackage hryvinskyi/magento2-invisible-captcha-recaptcha-replace replaces
every magento/module-re-captcha-* package (Two-Factor Auth and security.txt
from magento/security-package are left untouched). Caution: other installed
code may compile against the reCAPTCHA API modules — notably the bundled
PayPal Braintree module implements
Magento\ReCaptchaWebapiApi\Api\WebapiValidationConfigProviderInterface, so
removing all 25 packages breaks setup:di:compile on standard installs. On such
stores prefer the config-level disable above.
CLI
bin/magento hryvinskyi:invisible-captcha:disable [global|frontend|adminhtml] [--website_id=N] bin/magento hryvinskyi:invisible-captcha:migrate-recaptcha [--dry-run] [--force]