Table of contents

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.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

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