How do I handle page redirects in Playwright?

Playwright automatically follows redirects by default, but you can also monitor, control, and handle redirects explicitly depending on your testing needs.

Default Behavior

By default, page.goto() follows all redirects automatically and resolves when the final page loads:

# Python - Automatic redirect following (default behavior)
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()

    # This will follow all redirects automatically
    response = page.goto('https://httpbin.org/redirect/3')
    print(f"Final URL: {page.url}")
    print(f"Status: {response.status}")

    browser.close()
// JavaScript - Automatic redirect following (default behavior)
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  // This will follow all redirects automatically
  const response = await page.goto('https://httpbin.org/redirect/3');
  console.log(`Final URL: ${page.url()}`);
  console.log(`Status: ${response.status()}`);

  await browser.close();
})();

Monitoring Redirects

To track redirect chains and intermediate responses, listen to the response event:

# Python - Monitor all responses including redirects
from playwright.sync_api import sync_playwright

def handle_response(response):
    if 300 <= response.status < 400:
        print(f"Redirect: {response.status} from {response.url}")
        location = response.headers.get('location', 'No location header')
        print(f"  -> Location: {location}")
    else:
        print(f"Final response: {response.status} - {response.url}")

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()

    page.on("response", handle_response)
    page.goto('https://httpbin.org/redirect/2')

    browser.close()
// JavaScript - Monitor all responses including redirects
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  page.on('response', response => {
    if (response.status() >= 300 && response.status() < 400) {
      console.log(`Redirect: ${response.status()} from ${response.url()}`);
      const location = response.headers()['location'] || 'No location header';
      console.log(`  -> Location: ${location}`);
    } else {
      console.log(`Final response: ${response.status()} - ${response.url()}`);
    }
  });

  await page.goto('https://httpbin.org/redirect/2');
  await browser.close();
})();

Disabling Automatic Redirects

To prevent automatic redirect following and handle redirects manually:

# Python - Disable automatic redirects
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    context = browser.new_context()

    # Intercept and block redirects
    def handle_route(route):
        response = route.fetch()
        if 300 <= response.status < 400:
            print(f"Blocked redirect: {response.status}")
            route.fulfill(
                status=200,
                body=f"Redirect blocked. Would redirect to: {response.headers.get('location', 'unknown')}"
            )
        else:
            route.continue_()

    context.route("**/*", handle_route)
    page = context.new_page()

    page.goto('https://httpbin.org/redirect/1')
    browser.close()
// JavaScript - Disable automatic redirects
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext();

  // Intercept and block redirects
  await context.route('**/*', async route => {
    const response = await route.fetch();
    if (response.status() >= 300 && response.status() < 400) {
      console.log(`Blocked redirect: ${response.status()}`);
      await route.fulfill({
        status: 200,
        body: `Redirect blocked. Would redirect to: ${response.headers()['location'] || 'unknown'}`
      });
    } else {
      await route.continue();
    }
  });

  const page = await context.newPage();
  await page.goto('https://httpbin.org/redirect/1');
  await browser.close();
})();

Handling Specific Redirect Scenarios

Maximum Redirect Limits

# Python - Handle too many redirects
from playwright.sync_api import sync_playwright, TimeoutError

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()

    try:
        # This will follow up to ~20 redirects by default
        response = page.goto('https://httpbin.org/redirect/25', timeout=10000)
    except TimeoutError:
        print("Request timed out - possibly too many redirects")
    except Exception as e:
        print(f"Error: {e}")

    browser.close()

Conditional Redirect Handling

# Python - Handle redirects conditionally
from playwright.sync_api import sync_playwright

redirect_count = 0
max_redirects = 3

def handle_response(response):
    global redirect_count
    if 300 <= response.status < 400:
        redirect_count += 1
        print(f"Redirect #{redirect_count}: {response.status} - {response.url}")

        if redirect_count > max_redirects:
            print("Too many redirects!")
            # In a real scenario, you might want to stop the navigation
            return

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()

    page.on("response", handle_response)
    page.goto('https://httpbin.org/redirect/2')

    print(f"Final URL: {page.url}")
    browser.close()

Common Redirect Status Codes

  • 301: Moved Permanently
  • 302: Found (temporary redirect)
  • 303: See Other
  • 307: Temporary Redirect
  • 308: Permanent Redirect

Understanding these status codes helps you handle different redirect scenarios appropriately in your automation scripts.

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon