Playwright vs. Puppeteer: Proxy Architecture Differences
While both tools automate Chromium, Firefox, and WebKit, their proxy support models differ significantly:
- Playwright: Supports configuring proxies both at the browser level (via
chromium.launch({ proxy })) and independently for each BrowserContext (viabrowser.newContext({ proxy })). Credentials (username,password) are configured directly in the launch config. - Puppeteer: Passes the proxy host as a command-line Chromium flag (
--proxy-server=http://host:port) and handles authentication via an asynchronous listener on the page:page.authenticate({ username, password }).
1. Playwright Python Integration
In Python, pass your backconnect proxy dictionary to async_playwright().chromium.launch(). The following script connects to a rotating residential gateway, opens an isolated context, and verifies the IP:
import asyncio
from playwright.async_api import async_playwright
async def run_scraper():
# 2Captcha Residential Proxy endpoint (automatic IP rotation per connection)
proxy_config = {
"server": "http://proxy.2captcha.com:8080",
"username": "customer-user_id-zone-res",
"password": "your_secure_password"
}
async with async_playwright() as p:
# Launch Chromium with proxy configured at the browser level
browser = await p.chromium.launch(
headless=True,
proxy=proxy_config,
args=[
"--disable-blink-features=AutomationControlled",
"--no-first-run"
]
)
# Create isolated browser context with matching locale and timezone
context = await browser.new_context(
locale="en-US",
timezone_id="America/New_York",
viewport={"width": 1920, "height": 1080}
)
page = await context.new_page()
# Verify egress IP address
await page.goto("https://2-proxy.com/my-ip/")
print("Connected via Proxy! Page Title:", await page.title())
await browser.close()
if __name__ == "__main__":
asyncio.run(run_scraper())2. Playwright Node.js / TypeScript Integration
In Node.js, Playwright's multi-context architecture allows you to run multiple concurrent scraping threads from a single browser instance, saving memory and CPU:
import { chromium } from 'playwright';
async function scrapeWithProxy() {
const browser = await chromium.launch({
proxy: {
server: 'http://proxy.2captcha.com:8080',
username: 'customer-user_id-zone-res',
password: 'your_secure_password'
}
});
// Multiple isolated browser contexts can share the pool or specify individual proxies
const context = await browser.newContext({
permissions: ['geolocation'],
geolocation: { latitude: 40.7128, longitude: -74.0060 }
});
const page = await context.newPage();
await page.goto('https://httpbin.org/ip');
console.log(await page.textContent('body'));
await browser.close();
}
scrapeWithProxy();3. Puppeteer Node.js Integration
In Puppeteer, pass the proxy server through the args array and authenticate before loading the first URL:
const puppeteer = require('puppeteer');
async function scrapeWithPuppeteer() {
// Launch browser pointing to backconnect proxy server
const browser = await puppeteer.launch({
headless: "new",
args: ['--proxy-server=http://proxy.2captcha.com:8080']
});
const page = await browser.newPage();
// Authenticate proxy on the page instance
await page.authenticate({
username: 'customer-user_id-zone-res',
password: 'your_secure_password'
});
await page.goto('https://2-proxy.com/my-ip/');
console.log('Successfully navigated via proxy');
await browser.close();
}
scrapeWithPuppeteer();Preventing WebRTC IP Leaks
Even when routing all HTTP requests through a residential proxy, Chromium's built-in WebRTC (Web Real-Time Communication) feature can expose your real host public and local IP addresses to anti-bot inspection scripts through STUN candidate requests.
To prevent WebRTC leaks in headless Chromium:
--enforce-webrtc-ip-permission-check
You can test your browser configuration for leaks instantly using our free interactive tool: What Is My IP & WebRTC Leak Detector.