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.