How to bypass captcha using solving service in Cypress
This article shows how to integrate a captcha solving API into Cypress, allowing automated bypass of reCAPTCHA and other challenges directly inside E2E tests.
Why captcha is a problem in Cypress
Cypress is a popular tool for end-to-end (E2E) testing. It runs tests inside the browser and lets you validate real user flows: clicks, input, navigation, form submission, etc.
Common use cases:
- testing signup/login forms
- validating UI and business logic
- automating SPA testing (React, Vue, Angular)
- running tests in CI/CD pipelines (GitHub Actions, GitLab CI, etc)
But captcha breaks Cypress hard. Why? Because Cypress doesn’t control the browser at API level like Puppeteer or Playwright. It works through the DOM. This makes it powerless against:
- reCAPTCHA — requires JavaScript token validation
- Cloudflare Turnstile — uses anti-bot logic and strict browser fingerprinting
- text captchas — require OCR or manual solving
So, the captcha must be solved outside Cypress — and then the token needs to be injected back into the page.
Can’t you just disable captcha?
Sure. If it’s your site and you control the environment — disabling captcha in dev is the cleanest
way.
But in real life, things get messy:
- You’re testing production
- You don’t own the backend
- QA insists on real signup tests
- Staging environments mirror production with full captcha flows
That’s when automated solving becomes necessary.
Bypassing reCAPTCHA v2 in Cypress
We’ll use the 2Captcha API with a custom Node.js handler in cypress.config.js.
Here’s how it works:
- Cypress sends a solve request to 2Captcha
- Receives a taskId
- Polls every 5 seconds for the result
- Injects the returned token into the g-recaptcha-response field
cypress.config.js
const { defineConfig } = require("cypress");
const axios = require("axios");
require("dotenv").config();
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on("task", {
async solveCaptcha({ sitekey, pageurl, type = "recaptcha2", extra }) {
const API_KEY = process.env.CAPTCHA_KEY;
const createRes = await axios.post(
"https://api.2captcha.com/createTask",
{
clientKey: API_KEY,
task: {
type: "RecaptchaV2TaskProxyless",
websiteKey: sitekey,
websiteURL: pageurl,
...extra && extra
}
},
{
headers: { "Content-Type": "application/json" }
}
);
const taskId = createRes.data.taskId;
if (!taskId) throw new Error("No taskId returned");
return new Promise((resolve, reject) => {
const interval = setInterval(async () => {
try {
const resultRes = await axios.post(
"https://api.2captcha.com/getTaskResult",
{
clientKey: API_KEY,
taskId: taskId
},
{
headers: { "Content-Type": "application/json" }
}
);
if (resultRes.data.status === "ready") {
clearInterval(interval);
resolve(resultRes.data.solution.gRecaptchaResponse);
} else if (resultRes.data.status === "failed") {
clearInterval(interval);
reject(new Error("Captcha solving failed"));
}
} catch (err) {
clearInterval(interval);
reject(err);
}
}, 5000);
});
},
});
},
},
});
.env
CAPTCHA_KEY=your_api_key_here
Install:
npm install dotenv axios
Using the captcha token in your test
describe('Captcha bypass test', () => {
it('solves reCAPTCHA v2', () => {
const sitekey = '6Lc_aXkUAAAAA...';
const pageurl = 'https://example.com';
cy.task('solveCaptcha', { sitekey, pageurl }).then(token => {
cy.visit(pageurl);
cy.window().then(win => {
win.document.querySelector('#g-recaptcha-response').innerHTML = token;
});
cy.get('form').submit();
});
});
});
Cloudflare Turnstile support
Turnstile uses a different type value:
cy.task('solveCaptcha', {
type: 'turnstile',
sitekey: '0x4AAAAAA...',
pageurl: 'https://example.com'
}).then(token => {
cy.visit('https://example.com');
cy.window().then(win => {
win.document.querySelector('[name="cf-turnstile-response"]').value = token;
});
cy.get('form').submit();
});
reCAPTCHA v3 support
Requires action and minScore in the extra block:
cy.task('solveCaptcha', {
type: 'recaptcha3',
sitekey: '6Lc_aXkUAAAAA...',
pageurl: 'https://example.com',
extra: {
action: 'submit',
minScore: 0.3
}
}).then(token => {
cy.visit('https://example.com');
cy.window().then(win => {
win.document.querySelector('#g-recaptcha-response').innerHTML = token;
});
cy.get('form').submit();
});
Troubleshooting
taskId not returned
Check apikey, type, and sitekey values.
Status stuck on processing
Give it 20–30 seconds — some captchas take longer. Or the sitekey is invalid.
Status: failed
Try again. Might be a network issue or an invalid challenge.
Token inserted but form doesn't submit
Add cy.wait(1000) and make sure you run Cypress in headed mode. Some sites expect user behavior before validating the token.
CI-compatible
This approach works in GitHub Actions and GitLab CI — just make sure:
- Cypress runs in real browser mode (--headed)
- Page is loaded fully before injecting the token
- .env with CAPTCHA_KEY is passed in the job
Bonus: SolveCaptcha — a cheaper alternative
If you're solving lots of captchas and looking for cost savings, SolveCaptcha offers compatibility with the 2Captcha API at lower prices. Just replace the API key and you’re good to go.
Conclusion
Integrating captcha solving into Cypress lets your automated tests pass without manual input. With tools like 2Captcha, you can simulate a verified user experience and move through real-world flows, even behind protected forms.
If you’re working on CI pipelines, staging environments, or third-party app testing — this might just save your test suite from timing out at the worst possible moment.