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:

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:

  1. Cypress sends a solve request to 2Captcha
  2. Receives a taskId
  3. Polls every 5 seconds for the result
  4. 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.