Playwright is the go-to browser automation framework for developers who need reliable, cross-browser scraping and testing. Whether you are collecting product data from e-commerce sites, running geo-targeted tests, or automating multi-account workflows, proxies are essential for avoiding IP bans and accessing location-specific content. This guide walks you through every step of configuring proxies in Playwright -- from a single static IP to a fully rotating residential proxy pool -- with production-ready code examples you can copy and adapt.
Why You Need Proxies with Playwright
Playwright launches real browser instances (Chromium, Firefox, WebKit), which means target sites see a genuine browser fingerprint. That is an advantage over raw HTTP clients. But if every request originates from the same IP address, sites will detect the pattern and block you within minutes.
Proxies solve three problems at once:
- IP diversity -- Each request (or session) can use a different IP address, making your traffic indistinguishable from organic visitors.
- Geo-targeting -- Route traffic through IPs in specific countries or cities to see localized content, pricing, and search results.
- Session persistence -- Sticky sessions let you maintain the same IP across multiple page loads, which is critical for login flows and multi-step checkouts.
Prerequisites
Before writing any code, make sure you have:
- Node.js 18+ (for TypeScript/JavaScript examples) or Python 3.9+ (for Python examples)
- Playwright installed:
npm init playwright@latestorpip install playwright && playwright install - A Hex Proxies account with your gateway credentials (username and password from your dashboard)
Host: gate.hexproxies.com
Port: 8080 (HTTP/HTTPS) or 1080 (SOCKS5)
Step 1: Basic Proxy Configuration (TypeScript)
Playwright accepts proxy settings at the browser level through browserType.launch(). Here is the simplest possible setup:
import { chromium } from 'playwright';
async function main() {
const browser = await chromium.launch({
proxy: {
server: 'http://gate.hexproxies.com:8080',
username: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD',
},
});
const page = await browser.newPage();
await page.goto('https://httpbin.org/ip');
const body = await page.textContent('body');
console.log('Proxy IP:', body);
await browser.close();
}
main();
This launches a Chromium instance that routes all traffic through the Hex Proxies gateway. Every request from every page in this browser context will use the proxy.
What happens under the hood
- Playwright starts a Chromium process with
--proxy-server=http://gate.hexproxies.com:8080. - When the browser makes a request, the Hex gateway authenticates your credentials and assigns an IP from your pool.
- By default, each request may receive a different IP (rotating mode).
Step 2: Per-Context Proxy Configuration
If you need different proxies for different tasks -- for example, one context scraping US content and another scraping UK content -- use browser.newContext() with proxy overrides:
import { chromium } from 'playwright';
async function multiGeoScrape() {
const browser = await chromium.launch({
proxy: {
server: 'http://gate.hexproxies.com:8080',
},
});
// US context
const usContext = await browser.newContext({
proxy: {
server: 'http://gate.hexproxies.com:8080',
username: 'YOUR_USERNAME-country-us',
password: 'YOUR_PASSWORD',
},
});
// UK context
const ukContext = await browser.newContext({
proxy: {
server: 'http://gate.hexproxies.com:8080',
username: 'YOUR_USERNAME-country-gb',
password: 'YOUR_PASSWORD',
},
});
const usPage = await usContext.newPage();
const ukPage = await ukContext.newPage();
await usPage.goto('https://httpbin.org/ip');
await ukPage.goto('https://httpbin.org/ip');
console.log('US IP:', await usPage.textContent('body'));
console.log('UK IP:', await ukPage.textContent('body'));
await browser.close();
}
multiGeoScrape();
The geo-targeting parameter (-country-us, -country-gb) is appended to your username. Hex Proxies supports 93+ country locations with city-level targeting available for major markets.
Step 3: Sticky Sessions for Login Flows
When scraping sites that require authentication, you need the same IP address across the login page, credential submission, and subsequent authenticated pages. A rotating IP would cause the session to break.
Hex Proxies supports sticky sessions by appending a session identifier to your username:
import { chromium } from 'playwright';
async function stickySessionScrape() {
const sessionId = `session-${Date.now()}`;
const browser = await chromium.launch({
proxy: {
server: 'http://gate.hexproxies.com:8080',
username: `YOUR_USERNAME-session-${sessionId}`,
password: 'YOUR_PASSWORD',
},
});
const page = await browser.newPage();
// Step 1: Navigate to login
await page.goto('https://example.com/login');
// Step 2: Fill credentials (same IP maintained)
await page.fill('#username', 'myuser');
await page.fill('#password', 'mypass');
await page.click('#submit');
// Step 3: Access authenticated content (still the same IP)
await page.waitForURL('**/dashboard');
const data = await page.textContent('.account-data');
console.log('Account data:', data);
await browser.close();
}
stickySessionScrape();
The session ID can be any string. As long as you use the same session ID, the gateway routes your requests through the same IP. Sessions persist for up to 30 minutes of inactivity. Read more about sticky sessions and when to use them.
Step 4: Python Configuration
Playwright's Python API uses the same proxy structure. Here is the equivalent setup:
from playwright.sync_api import sync_playwright
def main():
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={
"server": "http://gate.hexproxies.com:8080",
"username": "YOUR_USERNAME",
"password": "YOUR_PASSWORD",
}
)
page = browser.new_page()
page.goto("https://httpbin.org/ip")
print("Proxy IP:", page.text_content("body"))
browser.close()
if __name__ == "__main__":
main()
Async Python variant
For scraping at scale, use the async API to run multiple browser contexts concurrently:
import asyncio
from playwright.async_api import async_playwright
async def scrape_with_proxy():
async with async_playwright() as p:
browser = await p.chromium.launch(
proxy={
"server": "http://gate.hexproxies.com:8080",
"username": "YOUR_USERNAME",
"password": "YOUR_PASSWORD",
}
)
# Launch 5 pages concurrently
tasks = []
for i in range(5):
context = await browser.new_context()
page = await context.new_page()
tasks.append(page.goto(f"https://httpbin.org/ip"))
await asyncio.gather(*tasks)
await browser.close()
asyncio.run(scrape_with_proxy())
Step 5: SOCKS5 Proxy Support
Playwright also supports SOCKS5 proxies, which handle both TCP and UDP traffic and can be useful for specific protocols:
import { chromium } from 'playwright';
async function socks5Example() {
const browser = await chromium.launch({
proxy: {
server: 'socks5://gate.hexproxies.com:1080',
username: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD',
},
});
const page = await browser.newPage();
await page.goto('https://httpbin.org/ip');
console.log('SOCKS5 IP:', await page.textContent('body'));
await browser.close();
}
socks5Example();
For a deeper comparison of when to use HTTP versus SOCKS5, see our guide on proxy protocols.
Step 6: Error Handling and Retry Logic
Production scrapers need robust error handling. Proxy connections can occasionally time out or return errors. Here is a resilient pattern:
import { chromium, Browser, Page } from 'playwright';
async function scrapeWithRetry(
url: string,
maxRetries: number = 3
): Promise<string | null> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
let browser: Browser | null = null;
try {
browser = await chromium.launch({
proxy: {
server: 'http://gate.hexproxies.com:8080',
username: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD',
},
});
const page: Page = await browser.newPage();
page.setDefaultTimeout(30000);
const response = await page.goto(url, {
waitUntil: 'domcontentloaded',
});
if (response && response.status() === 200) {
const content = await page.content();
return content;
}
console.warn(
`Attempt ${attempt}: received status ${response?.status()}`
);
} catch (error) {
console.error(`Attempt ${attempt} failed:`, error);
} finally {
if (browser) await browser.close();
}
// Wait before retrying with exponential backoff
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
return null;
}
Key patterns in this code:
- Exponential backoff: Each retry waits longer, reducing pressure on both the proxy and target site.
- Timeout configuration: The 30-second timeout prevents hanging connections from blocking your pipeline.
- Graceful cleanup: The
finallyblock ensures browser instances are always closed, preventing resource leaks.
Step 7: Headless vs Headed Mode
By default, Playwright runs in headless mode. Some anti-bot systems detect headless browsers, so you may need headed mode for certain targets:
const browser = await chromium.launch({
headless: false, // Visible browser window
proxy: {
server: 'http://gate.hexproxies.com:8080',
username: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD',
},
});
For production scraping, headless mode is more efficient. But if you are debugging why a particular site blocks your requests, switching to headed mode lets you see exactly what the browser sees.
Performance Tips for Proxy-Based Scraping
- Reuse browser contexts instead of launching new browsers for each URL. Launching a browser is expensive (500ms-2s). Creating a new context within an existing browser takes under 50ms.
- Block unnecessary resources to save bandwidth and speed up page loads:
await page.route('**/*.{png,jpg,jpeg,gif,svg,css,woff,woff2}', (route) =>
route.abort()
);
- Use residential proxies for detection-sensitive targets and ISP proxies for speed-critical tasks. Hex Proxies residential plans start at $4.25/GB; ISP proxies offer unlimited bandwidth with dedicated IPs.
- Rotate user agents alongside proxy rotation for a more natural fingerprint:
const context = await browser.newContext({
userAgent:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
});
- Monitor your success rate in the Hex Proxies dashboard. If your success rate drops below 95%, consider adjusting your rotation interval or switching proxy types.
Common Issues and Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
net::ERR_PROXY_CONNECTION_FAILED | Incorrect proxy host or port | Verify gate.hexproxies.com:8080 is correct |
407 Proxy Authentication Required | Wrong credentials | Check username/password in your dashboard |
| Pages load but show CAPTCHA | Target site detecting automation | Add random delays, rotate user agents, try residential IPs |
| Timeout errors | Target site slow or proxy overloaded | Increase timeout, retry with backoff |
| Different content than expected | Geo-targeting not applied | Append -country-XX to username |
Integrating with Your Existing Playwright Test Suite
If you already use Playwright for testing and want to add proxy support for geo-testing, you can configure proxies in your playwright.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
proxy: {
server: 'http://gate.hexproxies.com:8080',
username: process.env.HEX_PROXY_USER,
password: process.env.HEX_PROXY_PASS,
},
},
});
Store credentials in environment variables rather than hardcoding them. This configuration applies the proxy to every test in your suite.
Next Steps
You now have everything you need to run Playwright through proxies at any scale. For more integration examples, explore:
- Node.js Playwright proxy code snippets -- copy-paste examples for common patterns
- Playwright platform guide -- advanced configuration and optimization
- Python Requests proxy guide -- if you need a lighter-weight alternative for non-JavaScript rendering tasks
Ready to start scraping with Playwright?
Sign up for Hex Proxies and get access to 10M+ residential IPs and 250K+ ISP proxies. Residential plans start at $4.25/GB with no bandwidth caps. ISP plans include unlimited bandwidth with dedicated static IPs. Configure your Playwright scripts in under 5 minutes with the examples above.