
Proxies for Headless Browsers (Playwright, Puppeteer): Working Configurations
Learn how to set up proxies in Playwright and Puppeteer. Step-by-step guide with working code examples, proxy authentication, SOCKS5, and leak protection.
Proxychi
Browser automation with Playwright or Puppeteer without a proper proxy infrastructure quickly runs into strict anti-fraud and anti-bot restrictions. Modern protection systems such as Cloudflare, Akamai, and DataDome analyze not only browser fingerprints but also the reputation of the IP address from which requests originate. Without properly configuring the network layer, almost any headless automation script can be detected after a few iterations, resulting in repeated CAPTCHAs or complete access blocks.
Proper proxy integration with Puppeteer or Playwright makes it possible to distribute traffic, maintain persistent sessions, and simulate users connecting from different geographic locations. In this guide, we will look at practical proxy configuration and authentication methods for both frameworks.
Proxy Configuration in Puppeteer
In Puppeteer, network proxy settings are configured when launching the Chromium instance by passing command-line arguments to the browser.
Passing a Proxy Through Launch Arguments
The --proxy-server flag is used to configure a proxy. The basic syntax looks like this:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--proxy-server=http://185.130.5.15:8080']
});
const page = await browser.newPage();
await page.goto('https://httpbin.org/ip');
await browser.close();
})();
Proxy Authentication with Username and Password
Chromium does not support passing credentials in the http://user:pass@ip:port format directly through the --proxy-server argument. Instead, Puppeteer provides the page.authenticate() method:
const browser = await puppeteer.launch({
headless: true,
args: ['--proxy-server=http://185.130.5.15:8080']
});
const page = await browser.newPage();
// Authenticate with the proxy username and password
await page.authenticate({
username: 'proxy_user',
password: 'proxy_password'
});
await page.goto('https://httpbin.org/ip');
SOCKS5 Proxy Considerations
For SOCKS5 proxies, use the appropriate protocol prefix in the launch arguments:
--proxy-server=socks5://185.130.5.15:1080
If the SOCKS5 proxy requires authentication, page.authenticate() may behave inconsistently with some Chromium versions. In such cases, third-party plugins or routing the connection through a local tunnel may be required.
Proxy Configuration in Playwright
Playwright provides a more flexible architecture for handling network proxies. Unlike Puppeteer, where the proxy is typically configured at the browser-process level, Playwright allows proxy configurations to be defined for the browser as well as for individual isolated browser contexts (BrowserContext).
Configuring a Proxy at the Context Level
The proxy object can be passed directly to launch or newContext, with authentication credentials specified within the same configuration object:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
proxy: {
server: 'http://185.130.5.15:8080',
username: 'proxy_user',
password: 'proxy_password'
}
});
// Each context can have its own isolated browser state
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://httpbin.org/ip');
await browser.close();
})();
Playwright SOCKS5 Proxy Support
Playwright supports authenticated SOCKS5 proxies natively. Simply specify the socks5:// protocol prefix in the server parameter:
const context = await browser.newContext({
proxy: {
server: 'socks5://185.130.5.15:1080',
username: 'proxy_user',
password: 'proxy_password'
}
});
Architecture Considerations and Leak Prevention
Sticky Sessions vs. Rotating Proxies
When automating browser traffic, the right proxy rotation strategy depends on the task:
- Rotating Proxies (changing the IP for each request): Ideal for large-scale scraping of publicly available data.
- Sticky Sessions (keeping the same IP throughout a session): Recommended for authenticated accounts, shopping carts, or multi-step forms where a sudden IP change can trigger additional security checks.
Preventing Real IP Leaks and Browser Fingerprinting
Using a proxy does not automatically guarantee anonymity. The browser itself can still expose information about the underlying connection through mechanisms such as WebRTC or browser-specific headers.
By default, WebRTC may perform STUN requests through network paths that bypass the proxy, potentially exposing the device's real IP address.
To reduce the risk of IP leaks in Puppeteer, developers may use fingerprint-masking solutions such as puppeteer-extra-plugin-stealth and disable WebRTC-related functionality through Chromium launch arguments:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch({
args: [
'--proxy-server=http://185.130.5.15:8080',
'--disable-webrtc',
'--enforce-webrtc-ip-permission-check'
]
});
For reliable access in environments with strict anti-bot protection, the quality of the proxy infrastructure is critical. StableProxy provides residential and ISP proxies with support for HTTP/HTTPS and SOCKS5, designed to minimize data leaks and provide strong IP reputation across protected environments.
Puppeteer vs. Playwright: Proxy Configuration Comparison
| Configuration Parameter | Puppeteer | Playwright |
|---|---|---|
| Proxy configuration level | Globally at browser-process launch | At the browser or individual BrowserContext level |
| Authentication | Through the separate page.authenticate() method |
Built directly into the proxy configuration object |
| SOCKS5 with authentication | May require additional workarounds or plugins | Supported natively |
| IP isolation between tabs/contexts | More complex; may require separate processes | Straightforward with separate BrowserContext instances |
Frequently Asked Questions
Why doesn't page.authenticate() in Puppeteer work for background requests or Web Workers?
The Puppeteer page.authenticate() method handles HTTP authentication at the page level. Background requests, service workers, and network requests initiated before the document has fully loaded may be sent before the authentication handler is applied. A common solution is to use a proxy configured with IP allowlisting (IP Whitelisting) or to implement network interception at the CDP (Chrome DevTools Protocol) level.
How can I force DNS requests through a SOCKS5 proxy in Headless Chromium?
By default, Chromium may attempt to resolve domain names using the host system's DNS server rather than the proxy node. To route DNS resolution through a SOCKS5 proxy, add the following flag to the Chromium launch arguments: --host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE 127.0.0.1" Alternatively, use the socks5h:// prefix instead of socks5://. The h suffix indicates that DNS resolution should be delegated to the proxy server.
Why does Cloudflare still block a headless browser even when using a clean residential proxy?
Changing the IP address only addresses one aspect of network-level reputation. Cloudflare and other anti-bot systems can also analyze TLS fingerprints such as JA3/JA4, JavaScript-based browser fingerprints, Canvas, WebGL, AudioContext, and signals such as the navigator.webdriver property. If the TLS handshake or browser environment exposes a recognizable Node.js or standard Headless Chromium fingerprint, the request may still be rejected regardless of the proxy's IP reputation.
Can I dynamically change the proxy in Playwright without restarting the entire browser process?
Yes. Because Playwright supports proxy configuration at the BrowserContext level, you can create new browser contexts with different proxy configurations while keeping the same browser instance running. This can significantly reduce memory and CPU overhead when performing large-scale browser automation or scraping tasks.
How can I check whether my real IP address is leaking through WebRTC?
One of the simplest automated approaches is to initialize the proxy and then navigate to a dedicated IP-detection endpoint, such as https://api.ipify.org?format=json, or to a WebRTC leak-testing page. You can then read the resulting page content using page.content(). Another approach is to execute JavaScript inside the browser context with page.evaluate(). The script can create an RTCPeerConnection, collect ICE candidates, and check whether any of them expose the device's actual local or public IP address.
