# Using SOCKS5 proxies with Puppeteer and Playwright
Source: https://proxuno.com/blog/socks5-puppeteer-playwright
Updated: 2024-12-03

> Chromium does not support username and password authentication on SOCKS5 proxies. Three working setups — IP whitelisting, the HTTP port, and a local forwarder — with code for Puppeteer and Playwright, plus DNS and WebRTC leak checks.

Headless browsers are the most reliable way to collect from JavaScript-heavy sites, and SOCKS5 is often the protocol people reach for first. There is one catch that costs teams hours: **Chromium cannot authenticate to a SOCKS5 proxy with a username and password.** It accepts `socks5://host:port`, but ignores credentials, and authentication helpers such as Puppeteer's `page.authenticate()` only apply to HTTP proxies.

This guide shows three setups that work, how to verify there are no leaks, and when SOCKS5 is actually worth it.

## Do you need SOCKS5 at all?

For a browser loading web pages, the HTTP port of the proxy is functionally equivalent. HTTPS traffic is tunnelled end-to-end with `CONNECT`, so the proxy never sees page content, and the target sees exactly the same TLS handshake.

SOCKS5 is useful when:

- you also proxy non-HTTP TCP traffic from the same tool;
- a library or antidetect browser only exposes SOCKS settings;
- you want the proxy, not the client, to resolve hostnames (with SOCKS5, Chromium sends the hostname to the proxy and DNS is resolved on the exit side).

If none of these apply, use the HTTP port with credentials and skip to the leak checks.

## Setup 1: IP whitelisting (simplest)

All our products support IP-whitelist authentication as an alternative to username and password. Add the public IP of the machine running your browsers to the whitelist in the dashboard, and the proxy accepts connections from it without credentials. Chromium is then happy with a plain SOCKS5 URL.

On mobile proxies, the SOCKS5 port is the HTTP port plus 10,000: if your HTTP port is `12345`, SOCKS5 is `22345`.

**Puppeteer**

```javascript
const puppeteer = require('puppeteer');

const browser = await puppeteer.launch({
  args: ['--proxy-server=socks5://fr.mobile.{{gatewayDomain}}:22345'],
});
const page = await browser.newPage();
await page.goto('https://api.ipify.org');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
```

**Playwright**

```javascript
const { chromium } = require('playwright');

const browser = await chromium.launch({
  proxy: { server: 'socks5://fr.mobile.{{gatewayDomain}}:22345' },
});
const page = await browser.newPage();
await page.goto('https://api.ipify.org');
console.log(await page.textContent('body'));
await browser.close();
```

Whitelisting works well for fixed servers. It is less convenient on laptops or CI runners whose public IP changes.

## Setup 2: the HTTP port with credentials

If you cannot whitelist, use the HTTP port. Both frameworks handle HTTP proxy authentication natively.

**Puppeteer**

```javascript
const browser = await puppeteer.launch({
  args: ['--proxy-server=http://fr.mobile.{{gatewayDomain}}:12345'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'USER', password: 'PASS' });
await page.goto('https://example.com');
```

**Playwright** — per context, which is useful when each context should use a different proxy or residential session:

```javascript
const browser = await chromium.launch();
const context = await browser.newContext({
  proxy: { server: 'http://fr.mobile.{{gatewayDomain}}:12345', username: 'USER', password: 'PASS' },
});
```

## Setup 3: a local forwarder

When a tool insists on SOCKS5 and you cannot whitelist, run a small local proxy without authentication that forwards to the remote proxy with credentials. The browser talks to `127.0.0.1`, the forwarder handles authentication upstream.

In Node.js, the `proxy-chain` package does this in a few lines:

```javascript
const ProxyChain = require('proxy-chain');

const upstream = 'http://USER:PASS@fr.mobile.{{gatewayDomain}}:12345';
const local = await ProxyChain.anonymizeProxy(upstream);   // e.g. http://127.0.0.1:41235

const browser = await puppeteer.launch({ args: [`--proxy-server=${local}`] });
// …
await browser.close();
await ProxyChain.closeAnonymizedProxy(local, true);
```

The same idea works with any local forwarder. Bind it to `127.0.0.1` only — an unauthenticated proxy listening on a public interface is an open relay.

## Check for leaks

A misconfigured browser can reveal your real network in three ways. Test each one before running a job at scale.

### 1. The visible IP

Open an IP echo service through the proxy and compare it with the proxy's current IP in the dashboard.

### 2. DNS

With an HTTP proxy, hostnames in `CONNECT` requests are resolved by the proxy. With SOCKS5 in Chromium, hostnames are also sent to the proxy. Problems appear when another component (a local forwarder, a VPN client, a custom DNS-over-HTTPS setting) resolves names locally first. Use a DNS leak test page through the browser and check that the resolvers shown belong to the proxy's country and network, not yours.

### 3. WebRTC

WebRTC can discover local and public addresses through STUN over UDP, bypassing the proxy entirely. In automation, restrict it:

```javascript
args: [
  '--proxy-server=socks5://fr.mobile.{{gatewayDomain}}:22345',
  '--force-webrtc-ip-handling-policy=disable_non_proxied_udp',
],
```

With this policy, WebRTC only uses connections that go through the proxy. Most antidetect browsers expose an equivalent setting.

## Performance notes

- **Reuse browsers, not just pages.** Launching Chromium is the slowest step; keep one browser and create a context per identity.
- **Block what you do not need.** Request interception that aborts images, fonts and media cuts traffic — and residential cost — substantially on content-heavy sites.
- **Mind concurrency per IP.** Ten tabs on one mobile proxy look like ten users behind one CGNAT address, which is normal; a hundred do not.
- **Rotate between contexts, not during them.** On a mobile proxy, call the rotation API after closing a context and before opening the next one.

## Summary

| Situation | Setup |
|---|---|
| Fixed server, any protocol | IP whitelist + SOCKS5 or HTTP |
| Changing IP, browser only | HTTP port with credentials |
| Tool requires SOCKS5, cannot whitelist | Local forwarder |

The [Puppeteer and Playwright integration guide](/docs/integration-puppeteer-playwright) has complete, maintained examples, and the [IP whitelist documentation](/docs/ip-whitelist) explains how to add addresses and CIDR ranges.
