Captcha bypass tutorials

How to bypass captcha in Cypress

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:

  1. Cross-domain iframes: CAPTCHAs usually load inside an iframe from a different domain (e.g., google.com). Browser Same-Origin Policy and Cypress's default security settings block access to these elements.
  2. 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 Copy
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 Copy
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 Copy
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 Copy
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:

  1. Headless Mode Issues:
    Some CAPTCHAs behave more aggressively when the browser runs in headless mode (no UI). Try running the test with the --headed flag to see if it passes.

  2. Automation Detection:
    Advanced sites (using Akamai or Cloudflare) can detect the window.Cypress variable. In these cases, standard Cypress is not enough. You may need to use anti-detect plugins (like cypress-plugin-stripe) or run Cypress via a stealth browser.

  3. 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:

    javascript Copy
    cy.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 ensure chromeWebSecurity is disabled.