Périmètre sûr : ce guide couvre vos propres applications — développement, QA, préproduction, production — ou des systèmes pour lesquels vous détenez une autorisation écrite. Il ne traite pas de l'automatisation de sites tiers.
Dans une PWA, le défi CAPTCHA n'arrive jamais avec le HTML initial : il est injecté par le framework une fois le shell rendu, puis figé par le cache du service worker. Deux décisions règlent l'essentiel des incidents : servir les routes liées au CAPTCHA en network-only, et attendre un signal de rendu réel plutôt qu'un délai fixe. Le reste se traite comme n'importe quelle dépendance critique de votre application interne.
Ce qui change par rapport à une page classique
Sur un site rendu côté serveur, le widget est là dès la première réponse HTML. Une PWA déplace chaque étape.
- Le rendu est différé. Le widget reCAPTCHA v2 ou Cloudflare Turnstile apparaît après le montage du composant, pas au
load. - La navigation ne recharge rien. Votre code de détection doit se redéclencher à chaque changement de vue.
- Le service worker s'interpose. Il peut resservir une page mise en cache la veille, avec un sitekey qui n'est plus valide.
- Le DOM n'est pas la source de vérité. React, Vue ou Angular lisent la valeur du champ depuis leur état interne ; écrire dans l'input ne suffit pas toujours.
Sortez les routes CAPTCHA du cache du service worker
C'est le correctif qui élimine le plus de tickets. Toute requête liée à la vérification — widget, endpoint de validation — doit passer par le réseau. Les autres routes gardent leur stratégie habituelle (cache-first pour les ressources statiques, stale-while-revalidate pour les données peu sensibles).
Exemple de service worker :
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
if (url.pathname.startsWith('/api/captcha')) {
event.respondWith(fetch(event.request));
return;
}
// autres routes : cache-first, etc.
});
Versionnez le nom du cache à chaque déploiement et purgez les anciennes entrées dans activate. Sinon un poste peut garder pendant des jours un shell pointant vers un sitekey retiré, avec des échecs impossibles à reproduire en local.
Gardez le token en mémoire, jamais dans le stockage local
Le token a une durée de vie courte et aucune raison de survivre au rechargement de l'onglet. Gardez-le dans l'état du composant, transmettez-le au formulaire, effacez-le après la réponse du serveur. localStorage et sessionStorage allongent la fenêtre d'exposition côté client et laissent une trace lisible par toute extension installée.
Côté RGPD, le réflexe habituel s'applique : ne journalisez pas le token en clair, ne l'associez pas à un identifiant utilisateur dans vos traces, et minimisez les données personnelles collectées autour du formulaire protégé.
Détectez le widget après chaque changement de route
Le routage côté client casse toute détection déclenchée une seule fois au chargement. Instrumentez history.pushState ou l'événement de navigation de votre routeur, puis relancez la détection sur la nouvelle vue. Attendez la présence du sélecteur (.g-recaptcha, .cf-turnstile) avec un timeout explicite : un sleep de trois secondes tient sur votre machine et échoue sur un runner de CI chargé.
Après injection du token, déclenchez le callback ou la mise à jour d'état attendue par le framework : un formulaire React qui lit un état contrôlé enverra un champ vide si vous avez seulement modifié le DOM.
Exemple : une PWA interne de réservation de créneaux
Prenons une application interne de réservation de salles, utilisée par une équipe répartie entre Lyon et Bruxelles et hébergée chez OVHcloud, dont les workers de test tournent dans la région AWS eu-west-3 (Paris). Le formulaire de connexion est protégé par reCAPTCHA v2, celui de contact par Cloudflare Turnstile.
Symptôme observé : un taux d'échec de 20 % sur la connexion, uniquement en CI, parce que le service worker resservait le shell du build précédent. Après passage en network-only sur les routes de vérification et purge du cache versionné, la suite est redevenue stable. Pour ce volume — deux parcours, quelques dizaines d'exécutions par nuit — le plan BASIC ($15/mois, 5 threads) suffit ; une équipe qui parallélise ses tests sur plusieurs branches passera au plan STANDARD ($30/mois, 15 threads). La facturation se fait en dollars US et repose sur le nombre de threads simultanés, pas sur le nombre de résolutions.
Instrumentez chaque obtention de token
Tracez pour chaque appel : la durée entre l'envoi de la tâche et la réception du token, le code retour HTTP, l'identifiant de tâche et la taille de votre file d'attente. Ces quatre signaux suffisent à distinguer une lenteur de résolution d'un problème de rendu côté PWA.
Séparez les journaux par environnement et propagez un identifiant de corrélation dans votre traçage distribué (OpenTelemetry) : vous rejouerez un parcours complet à partir d'un seul identifiant.
Liste de contrôle avant mise en production
- Le périmètre reste limité à vos propres applications ou à des environnements autorisés par écrit.
- Les routes de vérification sont exclues du cache du service worker et le nom du cache est versionné.
- La clé API CaptchaAI vit dans un coffre ou un secret de CI.
- La détection se redéclenche à chaque changement de route.
- Le token est gardé en mémoire, effacé après usage, absent des journaux.
- Un retry avec backoff exponentiel borné couvre les erreurs transitoires.
Questions fréquentes
Pourquoi le widget reste-t-il vide après un changement de route ?
Parce que le script du widget n'est chargé qu'une fois, au premier rendu : sur la nouvelle vue, le conteneur existe mais aucun rendu n'a été demandé. Réinitialisez le widget au montage du composant, puis attendez le sitekey avant de lancer la résolution.
Faut-il désactiver le service worker pendant les tests automatisés ?
Non, et le désactiver vous fait perdre un environnement représentatif. Préférez un cache versionné par déploiement et des routes de vérification en network-only.
Où stocker le token pendant le parcours d'authentification ?
En mémoire, dans l'état du composant, le temps d'un envoi de formulaire. Effacez-le dès que le serveur a répondu : les tokens sont à usage unique et expirent vite.
CaptchaAI prend-il en charge hCaptcha dans une PWA ?
Non — hCaptcha n'est pas pris en charge, pas plus que FunCaptcha. Sont couverts reCAPTCHA v2 et v3, Cloudflare Turnstile, Cloudflare Challenge, GeeTest v3 et les CAPTCHA image, texte et en grille. GeeTest v4 est annoncé à venir ; CaptchaFox (bêta), Friendly Captcha (bêta) et Lemin (bêta) restent au stade bêta.
Quel plan choisir pour une suite de tests qui rejoue plusieurs parcours ?
Comptez en threads simultanés, pas en résolutions. Une suite séquentielle tient dans le plan BASIC ($15/mois, 5 threads) ; dès que plusieurs jobs de CI tournent en parallèle, le plan STANDARD ($30/mois, 15 threads) évite la file d'attente.
Guides connexes
- Le démarrage rapide de l'API CaptchaAI
- Tester le CAPTCHA dans des environnements autorisés
- Vérifier l'endpoint de résolution sur vos formulaires
- Intégrer la résolution CAPTCHA dans votre CI
- Résoudre reCAPTCHA v2 via l'API
- Résoudre Cloudflare Turnstile via l'API
Passez du correctif au cas par cas à une gestion reproductible du défi CAPTCHA. – Obtenez votre clé CaptchaAI.