Integrations menu
Developer guide

Puppeteer Proxy Setup: Auth, Rotation and SOCKS5 (Code)

To use an authenticated proxy in Puppeteer, launch Chrome with --proxy-server=http://gate.proxyfield.io:7000 and call page.authenticate({ username, password }) before the first page.goto(). ProxyField residential proxies cost from $1.95/GB ($2.95/GB for 1 GB), with a 99.6% success rate, according to our service data. Start with the Easy Trial: 1 GB for $2.95, no subscription, and the traffic never expires.

residential IPs
412M+
from, traffic never expires
$1.95/GB
Easy Trial
1 GB for $2.95
gate.proxyfield.io:7000
What goes in a Puppeteer script
Puppeteer settingProxyField value
--proxy-serverhttp://gate.proxyfield.io:7000, with no login in the flag: HTTP, HTTPS and SOCKS5 run on the same host and port
page.authenticate() username<login>-country-us-city-new_york; the optional keys are -country-<cc>, -region-<region>, -city-<city>, -session-<id> and -ttl-<seconds> (30 to 129,600 s, 20 min when ttl is left out); add -mobile or -dc right after the login for mobile or rotating datacenter IPs (-dc takes the country only)
page.authenticate() passwordyour ProxyField password
SOCKS5Chrome sends no SOCKS5 username or password and page.authenticate() does not apply to SOCKS5: use the allowlist, or a local forwarder such as proxy-chain with the same username and password; tools that take a proxy URL write socks5h://
No passwordadd the machine's IP to the allowlist (up to 100 IPv4 addresses) and use the port you set up in the dashboard; skip page.authenticate()
IP behavioura new IP on every request, or one IP for 30 s to 36 h with -session-<id>-ttl-<seconds>
Threadsunlimited concurrent threads
Headersthe gateway adds no Via, X-Forwarded-For or Forwarded header; the headers Puppeteer sends pass through; your Proxy-Authorization is removed before the request goes to the site
Credentialsin your account dashboard; the configurator generates as many proxy lines as you need

Set up Puppeteer with ProxyField

  1. Check the login once with curl (section 1).
  2. Launch Chrome with args: ['--proxy-server=http://gate.proxyfield.io:7000'], with no credentials in the flag.
  3. Call page.authenticate({ username, password }) on each new page before its first goto().
  4. Open https://api.ipify.org?format=json to see the exit IP.

The same login in a Puppeteer script (an ES module), with a New York exit IP.

JavaScript
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ args: ['--proxy-server=http://gate.proxyfield.io:7000'] });
const page = await browser.newPage();
await page.authenticate({ username: '<login>-country-us-city-new_york', password: '<password>' });
await page.goto('https://api.ipify.org');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();

Puppeteer's own reference for the login call: Page.authenticate() method (Puppeteer docs).

Mobile 5G/4G proxies cost $3.45/GB for 1 GB down to $2.45/GB from 1,000 GB on the same gateway and balance, over HTTP, HTTPS and SOCKS5.

The examples read your ProxyField username and password from two environment variables, PROXY_USER and PROXY_PASS, and print the exit IP from https://api.ipify.org. Puppeteer 25 needs Node.js 22.12 or later.

On this page 12 sections

1. Check the credentials with cURL

Run one request through the gateway before you open Puppeteer. If this fails, the problem is the credentials or your network, not your script.

Shell
curl -x gate.proxyfield.io:7000 -U "<login>-country-us-city-new_york:<password>" https://api.ipify.org

Replace <login> and <password> with your ProxyField username and password. You should see an IP that is not yours. Check its country with https://ipinfo.io/json, or test your proxy with the ProxyField proxy checker. More flags are in the cURL proxy guide.

2. Puppeteer proxy auth example: --proxy-server + page.authenticate()

Pass the gateway to Chrome with --proxy-server, then give the page your username and password with page.authenticate():

JavaScript
// npm i puppeteer
const puppeteer = require('puppeteer');

const { PROXY_USER, PROXY_PASS } = process.env; // your ProxyField username and password

(async () => {
  const browser = await puppeteer.launch({
    args: ['--proxy-server=http://gate.proxyfield.io:7000'],
  });
  const page = await browser.newPage();
  await page.authenticate({
    username: `${PROXY_USER}-country-us`,
    password: PROXY_PASS,
  });
  await page.goto('https://api.ipify.org?format=json');
  console.log(await page.evaluate(() => document.body.innerText));
  await browser.close();
})();

Save it as proxy.js and run it with your credentials:

Shell
PROXY_USER=<login> PROXY_PASS=<password> node proxy.js

It prints {"ip":"..."} with a US IP. Three rules keep it working:

  • No credentials in the flag. Chrome rejects a username and password inside --proxy-server: http://<login>:<password>@gate.proxyfield.io:7000 fails with net::ERR_NO_SUPPORTED_PROXIES. That is why page.authenticate exists.
  • Authenticate every page. Call page.authenticate() on each new page before its first goto(). A page without it fails with net::ERR_INVALID_AUTH_CREDENTIALS.
  • Targeting lives in the username. <login>-country-us exits in the United States; section 6 lists the other parameters.

To set a proxy for your everyday browser instead of a script, see Chrome proxy settings.

Which npm packages a Puppeteer proxy needs

Only puppeteer for an HTTP proxy with a username and password: --proxy-server and page.authenticate() are built in. Two more packages help in special cases.

PackageInstallWhen you need it
puppeteernpm i puppeteerAlways; it downloads a matching Chrome
proxy-chainnpm i proxy-chainSOCKS5 with a username and password, or one set of credentials for every page (section 5)
puppeteer-extranpm i puppeteer-extraPlugins such as stealth (section 8)

For Node.js requests without a browser, such as fetch, see the Node.js proxy setup .

3. One proxy per browser context

browser.createBrowserContext({ proxyServer }) gives each browser context its own proxy, so one browser can exit in several countries at once. The method is createBrowserContext() in Puppeteer 22 and later; older versions called it createIncognitoBrowserContext().

JavaScript
const puppeteer = require('puppeteer');

const { PROXY_USER, PROXY_PASS } = process.env;

async function checkIp(browser, country) {
  const context = await browser.createBrowserContext({
    proxyServer: 'http://gate.proxyfield.io:7000',
  });
  const page = await context.newPage();
  await page.authenticate({
    username: `${PROXY_USER}-country-${country}`,
    password: PROXY_PASS,
  });
  await page.goto('https://api.ipify.org?format=json');
  console.log(country, await page.evaluate(() => document.body.innerText));
  await context.close();
}

(async () => {
  const browser = await puppeteer.launch();
  await Promise.all([checkIp(browser, 'us'), checkIp(browser, 'gb')]);
  await browser.close();
})();

gb is the United Kingdom (<login>-country-gb). Countries take their two-letter code in lowercase, as in <login>-country-gb.

Each context has its own cookies, cache and connections, and page.authenticate() still runs once per page. The script prints two lines, one IP per country.

4. Rotate proxies so each page gets a different IP

With ProxyField you don't keep a proxy list: the gateway gives a new IP on every request, and a sticky session holds one IP from 30 s up to 36 h. In Puppeteer, the connection decides which IP you get.

Read moreShow less

Method A, a new page per URL, does not rotate reliably. Chrome keeps its connection to the proxy open and can reuse it for the next page in the same context, so on per-request rotation the next page can keep the same IP.

Method B, a new browser context per URL. A new context opens new connections, so the gateway can assign a new IP to each URL:

JavaScript
const puppeteer = require('puppeteer');

const { PROXY_USER, PROXY_PASS } = process.env;
const urls = [
  'https://api.ipify.org?format=json',
  'https://api.ipify.org?format=json',
  'https://api.ipify.org?format=json',
];

(async () => {
  const browser = await puppeteer.launch();
  for (const url of urls) {
    // A new context opens new connections, so the gateway can assign a new IP.
    const context = await browser.createBrowserContext({
      proxyServer: 'http://gate.proxyfield.io:7000',
    });
    const page = await context.newPage();
    await page.authenticate({ username: `${PROXY_USER}-country-us`, password: PROXY_PASS });
    await page.goto(url);
    console.log(await page.evaluate(() => document.body.innerText));
    await context.close();
  }
  await browser.close();
})();

Two things to know:

  • One page load opens several connections (the HTML, then scripts and images from other hosts). On per-request rotation, each of them can leave from a different IP. If a site ties a login or a cart to one IP, use a sticky session.
  • A sticky session takes a session parameter in the username: <login>-country-us plus -session-<id>-ttl-<seconds> after the location, as in <login>-country-us-session-he9f2qga-ttl-600 (ttl is 30 to 129,600 seconds, 20 minutes by default). It holds the IP from 30 s to 36 h, with presets of 1, 5, 10 and 30 min and 1, 3 and 6 h.

More on per-request rotation and sticky sessions.

5. SOCKS5 in Puppeteer

A socks5:// address in --proxy-server works, but only without credentials: Chrome can't send a SOCKS5 username and password, and page.authenticate() doesn't apply to SOCKS5. You have two options.

Option 1: allowlist your IP. Add the IP of the machine that runs Puppeteer to the allowlist in your dashboard, then connect without a username to the port you set up in the dashboard (<port> below):

JavaScript
const puppeteer = require('puppeteer');

(async () => {
  // Works when your IP is on the allowlist: Chrome sends no SOCKS5 credentials.
  // <port> is the port you set up in the dashboard for your allowlisted IP.
  const browser = await puppeteer.launch({
    args: ['--proxy-server=socks5://gate.proxyfield.io:<port>'],
  });
  const page = await browser.newPage();
  await page.goto('https://api.ipify.org?format=json');
  console.log(await page.evaluate(() => document.body.innerText));
  await browser.close();
})();
Read moreShow less

With an allowlisted IP there is no username to carry the country: with the allowlist, the product, location and session are set per port in the dashboard.

Option 2: run a local forwarder. The proxy-chain package starts a local proxy without authentication that forwards to the gateway with your credentials:

JavaScript
// npm i puppeteer proxy-chain
const puppeteer = require('puppeteer');
const proxyChain = require('proxy-chain');

const { PROXY_USER, PROXY_PASS } = process.env;

(async () => {
  // A local proxy without auth that forwards to the gateway with your credentials.
  const upstream = `socks5://${encodeURIComponent(PROXY_USER)}:${encodeURIComponent(PROXY_PASS)}@gate.proxyfield.io:7000`;
  const localProxy = await proxyChain.anonymizeProxy(upstream);

  const browser = await puppeteer.launch({ args: [`--proxy-server=${localProxy}`] });
  const page = await browser.newPage();
  await page.goto('https://api.ipify.org?format=json');
  console.log(await page.evaluate(() => document.body.innerText));
  await browser.close();
  await proxyChain.closeAnonymizedProxy(localProxy, true);
})();

encodeURIComponent keeps special characters in the password from breaking the URL (p@ss becomes p%40ss). proxy-chain accepts a socks5:// upstream with a username and password. SOCKS5 takes the same username and password. Replace socks5:// with http:// and the same forwarder sends your credentials over HTTP instead. See SOCKS5 proxies for when SOCKS5 is worth it.

6. Country, city and sticky sessions

Targeting goes in the username. ProxyField targets by country, region (state) and city, and covers all 50 US states.

GoalUsername
Any US IP, a new IP on every request<login>-country-us
An IP in New York City<login>-country-us-city-new_york
An IP in the United Kingdom<login>-country-gb
An IP in one US state<login>-country-us-region-new_york
One IP for 30 s to 36 h (sticky session)<login>-country-us-session-he9f2qga-ttl-600
Mobile 4G/5G instead of residentialresidential by default; add -mobile after the login for mobile IPs, as in <login>-mobile-country-us

ASN, ZIP and carrier targeting are not offered. See the product pages for residential proxies and mobile 4G/5G proxies.

7. Cut traffic: block images, fonts and media

Billing is per GB, so every image, font and video a page loads costs traffic. If your script needs only the HTML or the text, block the rest:

JavaScript
const puppeteer = require('puppeteer');

const { PROXY_USER, PROXY_PASS } = process.env;
const BLOCKED = ['image', 'font', 'media'];

(async () => {
  const browser = await puppeteer.launch({
    args: ['--proxy-server=http://gate.proxyfield.io:7000'],
  });
  const page = await browser.newPage();
  await page.authenticate({ username: `${PROXY_USER}-country-us`, password: PROXY_PASS });
  await page.setRequestInterception(true);
  page.on('request', (request) => {
    if (BLOCKED.includes(request.resourceType())) request.abort();
    else request.continue();
  });
  await page.goto('https://example.com');
  console.log(await page.title());
  await browser.close();
})();

Request interception works alongside page.authenticate(). The dashboard shows your usage in real time and the gigabytes you have left, so you can compare a run with and without blocking.

8. User agent, headers and stealth

Set the user agent with page.setUserAgent() and other headers with page.setExtraHTTPHeaders(), before page.goto():

JavaScript
const puppeteer = require('puppeteer');

const { PROXY_USER, PROXY_PASS } = process.env;

(async () => {
  const browser = await puppeteer.launch({
    args: ['--proxy-server=http://gate.proxyfield.io:7000'],
  });
  const page = await browser.newPage();
  await page.authenticate({ username: `${PROXY_USER}-country-us`, password: PROXY_PASS });
  await page.setUserAgent({
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36',
  });
  await page.setExtraHTTPHeaders({ 'Accept-Language': 'en-US,en;q=0.9' });
  await page.goto('https://httpbin.org/headers');
  console.log(await page.evaluate(() => document.body.innerText));
  await browser.close();
})();
Read moreShow less

In Puppeteer 25, page.setUserAgent() takes an options object; the older string form still works but is deprecated. Keep Accept-Language in line with the proxy country.

Sites can read headless Chrome's automation signals, such as navigator.webdriver; a proxy changes the IP, its location and its reputation, not those signals (our browser fingerprinting guide ).

puppeteer-extra with a proxy: stealth and user preferences plugins

puppeteer-extra wraps Puppeteer and adds plugins with puppeteer.use(). It launches Chrome the same way, so the proxy setup doesn't change: --proxy-server at launch, page.authenticate() on each page.

JavaScript
// npm i puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');

puppeteer.use(StealthPlugin());

const { PROXY_USER, PROXY_PASS } = process.env;

(async () => {
  const browser = await puppeteer.launch({
    args: ['--proxy-server=http://gate.proxyfield.io:7000'],
  });
  const page = await browser.newPage();
  await page.authenticate({ username: `${PROXY_USER}-country-us`, password: PROXY_PASS });
  await page.goto('https://api.ipify.org?format=json');
  console.log(await page.evaluate(() => document.body.innerText));
  await browser.close();
})();

puppeteer-extra-plugin-user-preferences writes Chrome profile preferences, such as the default font size, from a userPrefs object before launch. It sets browser preferences only; the proxy still goes in --proxy-server. If a bundled build (webpack, esbuild, a serverless package) fails with Cannot find module 'puppeteer-extra-plugin-user-preferences', install it as a direct dependency: puppeteer-extra loads plugins by name at runtime, and bundlers miss those imports. puppeteer-extra and its plugins were last released in 2023, so test them with your Puppeteer version.

9. Fix ERR_TUNNEL_CONNECTION_FAILED and other proxy errors

net::ERR_TUNNEL_CONNECTION_FAILED means Chrome reached the proxy, but the proxy answered the tunnel (CONNECT) request with an error instead of connecting to the site. Check the scheme, the credentials, whether the site is up, and whether it falls in a restricted category.

Read moreShow less
SymptomLikely causeFix
net::ERR_TUNNEL_CONNECTION_FAILEDThe proxy could not open the tunnel to the siteCheck the scheme (http:// or socks5://), the credentials and whether the site is up. Some site categories are restricted to protect the network, including financial and government services (our Acceptable Use Policy).
The page loads with status 407 (Proxy Authentication Required)Wrong username or password, or your IP is not on the allowlistCopy the credentials from the dashboard again and check the username parameters.
net::ERR_INVALID_AUTH_CREDENTIALSpage.authenticate() was not called on this page, or was called after goto()Call it on every new page before its first goto().
net::ERR_NO_SUPPORTED_PROXIESCredentials inside --proxy-server (http://<login>:<password>@... or socks5://<login>:<password>@...)Remove them; use page.authenticate, the allowlist or a local forwarder.
net::ERR_PROXY_CONNECTION_FAILED or a timeoutA wrong host or port, or a firewall that blocks port 7000, or the port you set up in the dashboard for the allowlistUse gate.proxyfield.io:7000 with your username and password (with the allowlist, the port you set up in the dashboard) and run the cURL check from section 1.
The IP is in the wrong countryA typo in the username, or a connection reused from an earlier pageCheck the spelling and open a new browser context.
The site loads without the proxyThe host is on --proxy-bypass-list or proxyBypassList, or it is localhost, which Chrome doesn't send through a proxy by defaultRemove the host from the bypass list and test with a public URL.

The gateway's own status codes: 407 Proxy Authentication Required for a wrong login or password, an IP not on the allowlist or a malformed location, 402 Payment Required when no traffic is left and 502 Bad Gateway when no exit could be connected; a restricted site gets no separate response, and a 403 comes from the site itself. The full list is in proxy errors.

10. Python

Puppeteer is a Node.js library; in Python, the Playwright proxy setup takes the same gateway (more in our Puppeteer guide ).

Get started

Start with the Easy Trial: 1 GB for $2.95, no minimum purchase, no subscription, and the traffic never expires. All purchases are final. See pricing per GB.

The same --proxy-server setup works in headless and headed mode; other headless tools are covered in our proxy setup hub and the alternatives to Puppeteer in our Puppeteer alternatives guide .

Puppeteer Proxy Setup FAQ

How do I authenticate a residential proxy in Puppeteer?

ProxyField residential proxies authenticate in Puppeteer with one call, from $1.95/GB: launch with --proxy-server=http://gate.proxyfield.io:7000, then call page.authenticate({ username, password }) on each page before goto(). Chrome rejects credentials written into the flag, so page.authenticate() is how you pass them.

How do I rotate proxies in Puppeteer so each page gets a different IP?

Open a new browser context per URL with createBrowserContext({ proxyServer }): a new context opens new connections, so the gateway assigns a new IP. Pages in one context can reuse an open connection and keep the same IP (section 4).

Why does Puppeteer fail with ERR_NO_SUPPORTED_PROXIES?

The --proxy-server flag holds a username and password, such as http://<login>:<password>@gate.proxyfield.io:7000, and Chrome rejects them there. Remove them and use page.authenticate, the IP allowlist or a local forwarder.

Why does Puppeteer return ERR_TUNNEL_CONNECTION_FAILED?

The proxy could not open the tunnel to the site: a wrong scheme, failed authentication, an unreachable site or a restricted site category. The checklist is in section 9 of this page.

Does Puppeteer support SOCKS5 proxies?

Yes, through a socks5:// address in --proxy-server, but without credentials. Use an IP allowlist with the port you set up in the dashboard, or a local forwarder such as proxy-chain that adds the username and password and connects to gate.proxyfield.io:7000 (section 5).

Does Puppeteer have built-in proxy support?

Puppeteer has no proxy of its own; it uses the proxy you give Chrome with --proxy-server, or one per browser context with createBrowserContext({ proxyServer }). page.authenticate() is built in for the username and password.

How do I use a different proxy per browser context in Puppeteer?

Pass proxyServer to browser.createBrowserContext() (Puppeteer 22 and later), then call page.authenticate() on each page in it. Each context can carry its own country in the username, so one browser can exit in several countries at once.

Can Puppeteer change the proxy without restarting the browser?

Yes. Open a new browser context with its own proxyServer, or authenticate a new page with another username, such as <login>-country-gb instead of <login>-country-us. The launch-level --proxy-server itself only changes with a new browser.

What type of proxy works best with Puppeteer?

Residential proxies for most public-data scraping, mobile 5G/4G proxies for targets that treat carrier IPs differently, and rotating datacenter proxies at $0.29/GB down to $0.22/GB for sites that accept datacenter IPs.

Do I need proxies with Puppeteer?

Not for a few pages, but a scraper that sends many requests from one IP soon meets rate limits, and some pages differ by country. A rotating gateway spreads the requests over many IPs and lets you pick the location.

Show 9 more questionsShow fewer questions

Does routing Puppeteer through a residential proxy slow down page loads?

A little: every request takes an extra hop through the gateway and the exit IP. Typical residential responses are under 1 s, according to our service data, and blocking images, fonts and media cuts both time and traffic (section 7).

Does headless browser scraping use more proxy traffic?

Yes: a full page load pulls the HTML plus scripts, styles, images, fonts and media, and all of it counts toward your GB. Block what your script doesn't need with request interception; the dashboard shows real-time usage to compare runs.

Can I use ProxyField proxies with Puppeteer or Playwright?

Yes, both. The same gateway and credentials work; only the place of the username differs: page.authenticate() in Puppeteer, the proxy option in Playwright.

Do I have to rewrite my Puppeteer code to use ProxyField?

No. Add the --proxy-server argument to puppeteer.launch() and one page.authenticate() call per page; the rest of your script stays the same.

What is puppeteer-extra, and does it work with a proxy?

puppeteer-extra is a wrapper around Puppeteer that adds plugins, such as stealth and user preferences, with puppeteer.use(). It launches Chrome the same way, so the proxy works unchanged; its last release dates from 2023.

What is puppeteer-extra-plugin-stealth?

A puppeteer-extra plugin that hides some of the automation signals headless Chrome exposes, such as navigator.webdriver. It was last released in 2023 and detection has moved on since, so test it with your targets; the proxy setup doesn't change.

Can proxies solve CAPTCHAs in Puppeteer?

No. A proxy changes the IP, its location and its reputation, and spreading requests over many IPs cuts rate-limit blocks. It does not solve CAPTCHAs or hide a headless browser's fingerprint or TLS signature, which Cloudflare, HUMAN (formerly PerimeterX) and DataDome also check.

How do I use a proxy with Pyppeteer?

The same way as in Puppeteer: args=['--proxy-server=http://gate.proxyfield.io:7000'] in launch() and await page.authenticate({...}) per page. Its PyPI page says the project is unmaintained (last release 2.0.0, February 2024), so Playwright for Python is the safer choice.

Are there free proxies for Puppeteer?

Public free proxy lists exist, but their IPs are shared, often slow or already blocked, and rarely take a username and password. ProxyField does not offer free proxies; the smallest purchase is 1 GB for $2.95.

Easy Trial: 1 GB for $2.95.

Try for the price of 1 GB, no subscription, traffic never expires.