The One Thing Most Puppeteer Proxy Guides Get Wrong
Chromium — the browser Puppeteer drives — does not read credentials from the --proxy-server value. So the obvious http://USER:PASS@HOST:PORT you'd use with curl or requests silently drops the USER:PASS part, and your first navigation fails with 407 Proxy Authentication Required.
The fix is two steps, not one:
- Pass only the host and port in launch args:
--proxy-server=PROXY_HOST:PORT. - Pass the username and password separately with
page.authenticate({ username, password }).
Get those two right and the rest is plain Puppeteer. With a dedicated German mobile endpoint you also skip the per-request rotation middleware that pool-based guides bolt on — you point the browser at one endpoint and rotate on demand at the proxy when you choose to.
Before You Start
You need four values from your GermanProxy.io account:
- Host and port of your endpoint
- Username and password
The examples use PROXY_HOST, PORT, USER and PASS as placeholders — replace them with your own. You'll also need Node.js 22.12+ (the current maintenance LTS that Puppeteer targets) and Puppeteer installed:
npm install puppeteer
New to running Puppeteer behind a proxy in Node generally? See the broader mobile proxy with Node.js guide.
Step 1 — Launch Chromium Through the Proxy (with auth)
This is the complete, runnable pattern. The proxy host and port go in args; the credentials go through page.authenticate:
const puppeteer = require("puppeteer");
// From your GermanProxy.io account
const PROXY_HOST = "PROXY_HOST";
const PORT = "PORT";
const USER = "USER";
const PASS = "PASS";
(async () => {
const browser = await puppeteer.launch({
args: [`--proxy-server=${PROXY_HOST}:${PORT}`],
});
const page = await browser.newPage();
// Chromium ignores user:pass in the proxy URL — authenticate here instead
await page.authenticate({ username: USER, password: PASS });
await page.goto("https://www.example.de", { waitUntil: "domcontentloaded" });
console.log("Title:", await page.title());
await browser.close();
})();
That is the whole setup. Every request from page — HTML, XHR, fetch, images — now travels over your German mobile IP. No pool middleware, no extra dependency.
Step 2 — Verify the IP Before You Scale
Confirm you're actually on a German mobile IP before you build anything on top of it. Point the same page at an IP-echo endpoint and read the result:
await page.goto("https://api.ipify.org?format=json", {
waitUntil: "domcontentloaded",
});
const ip = await page.evaluate(() => document.body.innerText);
console.log("Egress IP:", ip);
If it returns your own address, re-check that the host and port are in --proxy-server and that page.authenticate ran before the first goto. For a fuller checklist, see verifying your mobile proxy.
Step 3 — Check for WebRTC and DNS Leaks
A proxy routes Chromium's HTTP traffic, but the browser has two side channels that can expose your real address:
- WebRTC can surface your local IP via ICE candidates even with a proxy set — see what a WebRTC leak is.
- DNS can resolve names outside the tunnel depending on configuration — see what a DNS leak is.
For data-collection work in headless mode WebRTC is usually inert, but verify it for any flow where IP consistency matters. A real German mobile IP only helps if nothing else leaks past it.
HTTP vs. SOCKS5 in Puppeteer
Both protocols come with your endpoint. Which to pass in --proxy-server depends on what you need:
| Aspect | HTTP endpoint | SOCKS5 endpoint |
|---|---|---|
| Launch arg | --proxy-server=HOST:PORT |
--proxy-server=socks5://HOST:PORT |
| Username/password auth in Chromium | Yes, via page.authenticate |
No — Chromium has no SOCKS5 user/pass auth |
| Best auth mode | Credentials | IP allowlist |
| Typical use | Default for most scraping | Lower-level / non-HTTP traffic |
For most Puppeteer scraping, the HTTP endpoint with page.authenticate is the simplest path. Reach for SOCKS5 when you specifically need it and can use IP-allowlist authentication. Background: SOCKS5 vs HTTP proxy.
Sticky Sessions vs. Rotation
With a dedicated German endpoint you control the IP at the proxy, not in your code:
- Sticky session: keep one browser (or one
browser.createBrowserContext()) open across a multi-step flow — login, navigate, paginate — and the German mobile IP stays the same. - Rotation on demand: when you want a fresh IP, trigger rotation at the proxy and open a new context. There's no per-request IP pool to manage in Puppeteer.
This is why a dedicated endpoint keeps the code small: no rotation list, no health-checking, no retry-on-dead-IP loop. See sticky sessions and IP rotation explained and dedicated vs shared mobile proxy.
Why a Mobile IP for Puppeteer at All
Headless Chromium is a common choice for JavaScript-heavy pages that a plain HTTP client can't render — product pages, SERPs, dashboards. The catch is the egress IP: datacenter ranges are widely distrusted, so even a perfectly behaved crawler can hit friction. A real German mobile IP rides on carrier-grade NAT (CGNAT), the same shared-IP space thousands of ordinary phone users sit behind, which is why it carries higher IP reputation on German platforms.
For the trade-offs against other IP types, see mobile vs residential vs datacenter proxy and what is a mobile proxy.
Scrape Responsibly
A mobile proxy improves reliability for legitimate data collection — it is not a tool to circumvent access controls or platform rules. Keep your Puppeteer crawl clean and defensible:
- Respect each site's terms and its
robots.txt; collect only public data you're permitted to use. - Throttle with realistic delays between navigations so you don't overload servers — headless browsers are heavier than a plain HTTP client.
- Collect only what you need, and handle any personal data in line with the GDPR. See whether scraping is legal in Europe.
- Be honest about who you are — don't impersonate a service or misuse a target's security mechanisms.
Whether mobile proxies themselves are lawful to use: are mobile proxies legal.
FAQ
How do I set a proxy in Puppeteer?
Pass it as a Chromium launch argument: puppeteer.launch({ args: ['--proxy-server=PROXY_HOST:PORT'] }). For a proxy that needs a username and password, you must additionally call page.authenticate({ username, password }) — Chromium ignores credentials embedded in the --proxy-server URL.
Why does Puppeteer ignore the username and password in my proxy URL?
Chromium does not read credentials from the --proxy-server value, so http://USER:PASS@HOST:PORT silently drops the USER:PASS part. Supply the host and port in launch args and the credentials separately via page.authenticate({ username, password }). Skipping this returns 407 Proxy Authentication Required.
Can Puppeteer use a SOCKS5 mobile proxy?
Yes. Use --proxy-server=socks5://PROXY_HOST:PORT in the launch args. Chromium does not support SOCKS5 username/password auth, so SOCKS5 works best with IP-allowlist authentication; for credential auth on Chromium, use the HTTP endpoint with page.authenticate.
How do I rotate IPs with Puppeteer?
With a dedicated German endpoint you rotate on demand at the proxy, not per request in code. For a sticky session, keep one browser context open across the flow; to get a fresh IP, trigger rotation at the proxy and open a new context. See sticky sessions and IP rotation explained.
Should I check for WebRTC and DNS leaks?
Yes. A proxy routes HTTP traffic, but WebRTC can expose your real IP and DNS can resolve outside the tunnel. Verify both before you rely on the setup — see what a WebRTC leak is and what a DNS leak is.
Puppeteer or Playwright for a mobile proxy?
Both work. Playwright takes credentials directly in its proxy option, while Puppeteer splits host/port (launch args) from credentials (page.authenticate). If you're choosing, compare with the Playwright setup and Selenium setup.
CTA
Drive headless Chromium on real German mobile IPs. Get a German mobile proxy — HTTP/SOCKS5/OpenVPN, sticky sessions, on-demand rotation, fair-use 20 GB/day, from a transparent EU provider.
Get a German mobile proxy: Real 4G/5G IPs from Germany, fair-use 20 GB/day — browse German mobile proxy plans or try a shared 5G German proxy.