A common Puppeteer pattern is --proxy-server at launch, then page.authenticate on each page. Console credentials; fake placeholders. Not a Playwright or antidetect guide. Not for mainland China.
Copy protocol, host, port, and auth from the console, then put them in Puppeteer. A common pattern is `--proxy-server` for host and port at launch, then `page.authenticate` on every `page` before `goto`. That is not the same API as Playwright’s `launch({ proxy })`; see Playwright residential proxy. HTTP from Node: Axios setup. Antidetect field mapping: antidetect setup.
ZTProxy sells rotating residential (per GB) and static residential (per IP·month). Examples use fake placeholders—not production hosts. Authorized targets only. Not for mainland China.
What to have ready
| Ready item | Source |
|---|---|
| Protocol, host, port, auth | Console values only (not the fake placeholders) |
| Node.js and `puppeteer` | Your local environment |
| Check URL | A page you may access |
| Rotating or static order | Rotating vs static |
Placeholders (not production): host `proxy.example.invalid`, port `10000`, user `USER_EXAMPLE`, password `PASS_EXAMPLE_NOT_REAL`. Protocol and strings: HTTP / SOCKS5 setup, proxy format. Commands follow Docs EN quickstart.
Recommended pattern: launch arg plus per-page auth
Do not rely on putting `user:pass` inside the `--proxy-server` URL and hoping Chromium will honor it. Pass credentials with `page.authenticate`, and set them again on every new page, or later pages may hit 407 after the first success. Do not invent country or session fields in the username.
import puppeteer from "puppeteer";
const host = process.env.ZT_PROXY_HOST ?? "proxy.example.invalid";
const port = process.env.ZT_PROXY_PORT ?? "10000";
const username = process.env.ZT_PROXY_USER ?? "USER_EXAMPLE";
const password = process.env.ZT_PROXY_PASS ?? "PASS_EXAMPLE_NOT_REAL";
const browser = await puppeteer.launch({
args: [`--proxy-server=http://${host}:${port}`],
});
const page = await browser.newPage();
await page.authenticate({ username, password });
await page.goto("https://example.com", { timeout: 30_000 });
console.log(await page.title());
await browser.close();
Switch `--proxy-server` to a SOCKS form only when the console returns SOCKS. Do not assume UDP is available. If the username or password has special characters, follow what the console and `authenticate` require—do not invent an encoding scheme and then hide it in the launch argument.
Sessions and common issues
Multiple pages in one browser can still see rotating exits change. Connection reuse is not a static IP, and it is not automatically sticky. See sticky ≠ static.
| Symptom | Check first |
|---|---|
| 407 | Missing `authenticate`; not called again on a new page; order still valid |
| Still your machine IP | `--proxy-server` actually applied; no env or system proxy taking over |
| Timeouts | Protocol and port match the console; you did not leave the fake host in place |
| Want to isolate the library | Recheck the same credentials with curl |
Fuller symptom table: 407 and timeouts.
Compliance and scope
Authorized targets only. This is not a bypass or account-farming tutorial. Prices follow the console and pricing (rotating about $0.40/GB, about $0.20/GB on a 100 TB full bundle; static about $1.50 shared and about $5 dedicated per IP/month). USDT/USDC available; Visa/Mastercard in application. Not for mainland China.
FAQ
How is this different from the Playwright page?
Different library and API. Playwright can set host and auth together in `launch({ proxy })`. Puppeteer more often uses a launch argument plus `page.authenticate`.
Do I have to authenticate every new page?
Yes. Setting it only on the first page is a common cause of later 407s.
Can I use the example host as-is?
No. Replace it with console values.
Can I append a country code to the username myself?
Do not invent syntax. Use only fields the console or docs define.
If I reuse the browser, do I keep the same IP?
Not necessarily. Connection reuse is not static, and it is not automatically sticky.
Can I use this to bypass login limits?
No. Authorized targets only.
Do I need SOCKS5?
Follow the protocol the console returns. Many HTTPS pages work with HTTP `CONNECT`.
Where are prices confirmed?
In the console and on pricing.