How to bypass captcha in Cypress
This article explains how to integrate a captcha-solving API with Cypress to automatically bypass captchas during end-to-end tests.
Cypress is standard for E2E web testing. However, when your automated tests encounter a CAPTCHA (reCAPTCHA, Cloudflare Turnstile, etc.), the scenario breaks.
Cypress runs directly inside the browser, executing commands in the same run loop as your application. While this makes it fast, it creates two fundamental problems when dealing with CAPTCHAs:
- Cross-domain iframes: CAPTCHAs usually load inside an
iframefrom a different domain (e.g., google.com). Browser Same-Origin Policy and Cypress's default security settings block access to these elements. - Anti-bot Protection: A simple
.click()is rarely enough. Modern protection systems analyze mouse behavior, network fingerprints, and browser variables. Cypress interactions are often too "robotic" and linear.
In this guide, we will cover the full strategy: from configuring your test environment ("White Box") to using an external Cypress captcha solver for real-world scenarios.
Part 1: Best Practices (How NOT to Solve Captchas)
A professional QA approach implies that you should not waste resources solving real CAPTCHAs in Development or Staging environments. Instead, use bypass methods.
1. Using Google Test Keys
For reCAPTCHA v2, Google provides official test keys. These keys always render a "No CAPTCHA" widget, and verification passes instantly.
If you have access to the application source code, configure your test environment to use these credentials:
- Site key:
6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI - Secret key:
6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
2. Mocking Requests (Stubbing) with cy.intercept
The fastest way to "pass" a CAPTCHA in Cypress is to trick the frontend by mocking the server response. You can intercept the network request and return a success status.
javascript
describe('Login bypass', () => {
it('should verify user without real captcha', () => {
// Intercept the captcha validation request on your backend
cy.intercept('POST', '/api/validate-captcha', {
statusCode: 200,
body: { success: true, verificationPassed: true }
}).as('bypassCaptcha');
cy.visit('/login');
// ...fill in fields...
cy.get('#submit').click();
cy.wait('@bypassCaptcha');
});
});
Part 2: Cypress Captcha Solver (API Integration)
If you are testing Production, scraping external sites, or cannot disable the CAPTCHA, you need to integrate an API solver. You can use services like 2Captcha or SolveCaptcha to handle the solving process.
Since Cypress commands are synchronous and cannot wait for async events (like solving a captcha), we must move the logic to a Node.js process using cy.task.
Step 1: Security Configuration (Critical)
To allow Cypress to interact with CAPTCHA elements inside an iframe (e.g., to inject the token), you must disable Chrome Web Security.
In your cypress.config.js:
javascript
const { defineConfig } = require("cypress");
const axios = require("axios");
module.exports = defineConfig({
// IMPORTANT: Allows access to cross-domain iframes
chromeWebSecurity: false,
e2e: {
setupNodeEvents(on, config) {
on("task", {
async solveCaptcha({ sitekey, pageurl, apiKey, type = "recaptcha2" }) {
// Use 2Captcha or SolveCaptcha API endpoints here
const API_URL = "https://api.2captcha.com"; // or https://api.solvecaptcha.com
try {
// 1. Create Task
const { data: createData } = await axios.post(`${API_URL}/createTask`, {
clientKey: apiKey,
task: {
type: type === "turnstile" ? "TurnstileTaskProxyless" : "NoCaptchaTaskProxyless",
websiteKey: sitekey,
websiteURL: pageurl
}
});
if (!createData.taskId) throw new Error("TaskId not received");
// 2. Poll for Result
console.log(`Captcha task created: ${createData.taskId}`);
for (let i = 0; i < 24; i++) { // Wait up to 120 seconds
await new Promise(r => setTimeout(r, 5000)); // Wait 5 seconds
const { data: result } = await axios.post(`${API_URL}/getTaskResult`, {
clientKey: apiKey,
taskId: createData.taskId
});
if (result.status === "ready") {
return result.solution.gRecaptchaResponse || result.solution.token;
}
if (result.status === "failed") {
throw new Error("Captcha solving failed via API");
}
}
throw new Error("Timeout waiting for solution");
} catch (e) {
console.error(e);
return null;
}
}
});
}
}
});
Step 2: Automating the Bypass in Tests
Now that we have the solveCaptcha task, we can use it to perform captcha automation. We retrieve the token and forcibly inject it into the form's hidden field, simulating a successful check.
Example for reCAPTCHA v2:
javascript
describe('Automated Captcha Solver Test', () => {
it('bypasses reCAPTCHA automatically', () => {
const pageUrl = 'https://example.com/login';
const siteKey = '6Lc_aXkUAAAAA...'; // Find this in the page source (data-sitekey)
const apiKey = Cypress.env('CAPTCHA_API_KEY'); // Set this in cypress.env.json
cy.visit(pageUrl);
// Call the solver task
cy.task('solveCaptcha', { sitekey: siteKey, pageurl: pageUrl, apiKey: apiKey })
.then((token) => {
if (!token) throw new Error('Failed to retrieve captcha token');
// Inject token into the hidden field
cy.get('#g-recaptcha-response')
.invoke('attr', 'style', 'display: block') // Make visible for debugging
.invoke('val', token);
// Optional: Trigger a callback if the site requires it
// cy.window().then(win => win.onCaptchaSuccess(token));
cy.get('#submit-btn').click();
});
cy.url().should('include', '/dashboard');
});
});
Example for Cloudflare Turnstile:
The logic is identical, only the field selector changes:
javascript
cy.task('solveCaptcha', {
type: 'turnstile',
sitekey: '0x4AAAAAA...',
pageurl: 'https://example.com',
apiKey: Cypress.env('CAPTCHA_API_KEY')
}).then(token => {
cy.get('[name="cf-turnstile-response"]').invoke('val', token);
cy.get('form').submit();
});
Troubleshooting: What if it doesn't work?
Even with the correct code, anti-bot systems may block Cypress. Here is a checklist to fix common issues:
-
Headless Mode Issues:
Some CAPTCHAs behave more aggressively when the browser runs in headless mode (no UI). Try running the test with the--headedflag to see if it passes. -
Automation Detection:
Advanced sites (using Akamai or Cloudflare) can detect thewindow.Cypressvariable. In these cases, standard Cypress is not enough. You may need to use anti-detect plugins (likecypress-plugin-stripe) or run Cypress via a stealth browser. -
Form Not Submitting:
Modern frameworks (React, Angular, Vue) might not detect that you changed the hidden field value using.invoke('val'). You often need to trigger an event manually after injection:javascriptcy.get('#g-recaptcha-response').invoke('val', token).trigger('change');
Conclusion
Cypress captcha automation requires a strategic approach since the tool is designed for testing, not bypassing protection.
- For Staging/Dev, always use mocking (
cy.intercept) or Google's test keys. - For Production/Scraping, use reliable API solvers like 2Captcha or SolveCaptcha via
cy.task, and ensurechromeWebSecurityis disabled.