Skip to main content
[ DOCS / PLAYWRIGHT ]

Playwright proxy setup with authentication, in four languages

Playwright logs in to a proxy on its own: give it the gateway, your username and your password, and every page loads from a real home IP in the country you pick. No extension, no local forwarder, no SDK. The same code works in Node.js, Python, Java and .NET.

  • Built-in proxy loginusername and password in the proxy option
  • One IP per contextrun many identities in one browser
  • Real home IPs219 countries, down to the city

No plan yet? The smallest one is the test: 1 GB of Residential for $0.99, no subscription.

KEY FACTS

  • Playwright takes a proxy with server, username and password in launch() or in newContext(): server http://gw.ludaxproxy.com:7777, username resi-lx_k3m9q2p7xa, password YOUR_PASSWORD plus options.
  • A proxy set on a browser context applies to that context only, so one browser can run many contexts, each with its own IP. This works with no proxy at launch (tested with Playwright 1.64 on Windows).
  • A browser opens many connections per page. Without a session token each new connection can leave from a new IP, so give each context -session-<token>-time-<minutes>: one stable IP for everything that context loads.
  • Use the standard gateways (gw.ludaxproxy.com:7777, Premium gw.ludaxproxy.com:8080). A real browser already sends a real browser TLS handshake, so it does not need the TLS ports.
  • Playwright’s username and password are for HTTP proxies: use the HTTP gateway, not SOCKS5, when the proxy needs a login.
  • A gateway 502 shows as net::ERR_TUNNEL_CONNECTION_FAILED (retry it); a refused login as a navigation error such as net::ERR_HTTP_RESPONSE_CODE_FAILURE (fix the credentials).

Using an AI assistant? Give it this guide

One Markdown file with everything ChatGPT, Claude, Cursor or Copilot needs to write working Ludax code for Playwright: the connection format, the gateways in force, sessions, errors and tested examples. Copy it into the chat, or paste this prompt:

Read https://ludaxproxy.com/docs/playwright.md and use Ludax proxies in my Playwright project.

[ 01 / QUICKSTART ]

Set up a Playwright proxy in three steps

If the page prints a JSON answer with a residential IP, your Playwright proxy works. Pick your language once: every code block on this page follows it.

  1. Get your username and password

    In the dashboard, open Generate. Your username looks like lx_k3m9q2p7xa and gets the product prefix: resi- for Residential, premi- for Residential Premium. Each plan has its own 16-character password. The full format is in proxy authentication.

    No account yet? Get 1 GB of Residential for $0.99: an email address is all it takes, you pay in crypto, and you are ready in a minute.

  2. Install Playwright

    install_node.sh
    npm install playwright
    npx playwright install chromium
  3. Run this

    first_request.mjs
    import { chromium } from "playwright";
    
    const browser = await chromium.launch({
      proxy: {
        server: "http://gw.ludaxproxy.com:7777",
        username: "resi-lx_k3m9q2p7xa",
        password: "YOUR_PASSWORD-country-us",
      },
    });
    const page = await browser.newPage();
    await page.goto("https://api.ludaxproxy.com/whoami");
    console.log(await page.textContent("body"));
    await browser.close();

    You should see something like this, with your own exit IP:

    output
    {"ip": "203.0.113.24", "country": "United States", "type": "residential", "tls_profile": "Chrome 154", "latency_ms": 418}

    That is a real home connection in the United States, picked by -country-us in the password. tls_profile shows the browser your handshake matches: Chrome sends its own, so it needs nothing from the gateway. Change us to de, jp or any of the 181 country codes.

    Official docs: Playwright: HTTP proxy · browserType.launch()

[ 02 / CODE GENERATOR ]

Playwright proxy code generator

Choose the product, the country and whether the IP should stay the same, and copy the code for your language. Type your own username and password to get code you can run as is: they stay in this browser tab and are never sent anywhere.

Product

Real home IPs for everyday scraping. Gateway: gw.ludaxproxy.com:7777

IP address

YOUR CONNECTION STRING

http://resi-lx_k3m9q2p7xa:YOUR_PASSWORD-country-us@gw.ludaxproxy.com:7777
  • username
  • plan password
  • session
  • location
  • gateway
ludax.mjs
[ 03 / RECIPES ]

Playwright proxy recipes: contexts, rotation, geo and errors

Each one runs on its own: paste it, put in your username and password, run. They follow the language you picked above.

01

One IP per browser context: rotating proxies in Playwright

Several accounts or jobs in one browser, each from its own IP. A new context with a new session token gives a new IP; the same token keeps it.

contexts.mjs
import { chromium } from "playwright";
import { randomBytes } from "node:crypto";

const GATEWAY = "http://gw.ludaxproxy.com:7777";
const USER = "resi-lx_k3m9q2p7xa";
const PASS = "YOUR_PASSWORD";

const browser = await chromium.launch();
for (const job of ["account-1", "account-2", "account-3"]) {
  // a new session token = a new IP, held by this context for 30 minutes
  const token = randomBytes(4).toString("hex");
  const context = await browser.newContext({
    proxy: { server: GATEWAY, username: USER, password: `${PASS}-session-${token}-time-30-country-us` },
  });
  const page = await context.newPage();
  await page.goto("https://api.ludaxproxy.com/whoami");
  console.log(job, JSON.parse(await page.textContent("body")).ip);
  await context.close();
}
await browser.close();
output (your IPs will differ)
account-1 203.0.113.24
account-2 198.51.100.7
account-3 192.0.2.66

Why a token per context, and not simply no session? A page is dozens of connections: the document, scripts, images, API calls. Without a token each new connection can leave from a different IP, which no real visitor does. With one token per context everything that context loads shares one IP, and a new context starts fresh. Sessions hold up to 1000 minutes on Residential and Premium; always send -time-.

Official docs: browser.newContext(): proxy

02

Geo-targeting with a matching language and time zone

Local prices, search results and ads. A German IP with an American browser clock is a mismatch some sites notice: set the context’s locale and time zone to the exit’s.

geo.mjs
import { chromium } from "playwright";

const browser = await chromium.launch();
// an IP in Munich, and a browser that looks local: German language, Berlin time
const context = await browser.newContext({
  proxy: {
    server: "http://gw.ludaxproxy.com:7777",
    username: "resi-lx_k3m9q2p7xa",
    password: "YOUR_PASSWORD-session-muc01-time-30-country-de-state-by-city-munich",
  },
  locale: "de-DE",
  timezoneId: "Europe/Berlin",
});
const page = await context.newPage();
await page.goto("https://api.ludaxproxy.com/whoami");
console.log(await page.textContent("body"));
console.log(await page.evaluate(() => [navigator.language, Intl.DateTimeFormat().resolvedOptions().timeZone]));
await browser.close();
output
{"ip": "203.0.113.81", "country": "Germany", ...}
[ 'de-DE', 'Europe/Berlin' ]

Order matters in the password: session first, then -country-, -state-, -city-. Region and city codes for every country are in the locations directory, for example Germany and the United States.

03

Save bandwidth: block images, fonts and media

Plans are priced per GB. Most pages are mostly pictures and fonts that a scraper never reads; abort them in the browser and they never cross the proxy.

save_data.mjs
import { chromium } from "playwright";

const SKIP = new Set(["image", "font", "media"]);

const browser = await chromium.launch({
  proxy: {
    server: "http://gw.ludaxproxy.com:7777",
    username: "resi-lx_k3m9q2p7xa",
    password: "YOUR_PASSWORD-session-wiki01-time-10-country-us",
  },
});
const context = await browser.newContext();
// images, fonts and video never leave the browser, so they never count against your GB
await context.route("**/*", (route) =>
  SKIP.has(route.request().resourceType()) ? route.abort() : route.continue(),
);
const page = await context.newPage();
await page.goto("https://en.wikipedia.org/wiki/Proxy_server");
console.log(await page.title());
await browser.close();
output
Proxy server - Wikipedia

Official docs: Playwright: abort requests

04

Handle proxy errors: retry a 502, stop on a refused login

Production scrapers. A dropped exit is worth one more try; a refused login is not.

retry.mjs
import { chromium } from "playwright";

// Open a URL through Ludax: retry a dropped exit (502), stop at once on anything else.
async function open(page, url, tries = 3) {
  for (let attempt = 1; ; attempt++) {
    try {
      return await page.goto(url, { timeout: 30000 });
    } catch (error) {
      const dropped = error.message.includes("ERR_TUNNEL_CONNECTION_FAILED");
      if (!dropped || attempt === tries) throw error; // a refused login will not fix itself
      await new Promise((resolve) => setTimeout(resolve, 1000 * 2 ** (attempt - 1)));
    }
  }
}

const browser = await chromium.launch({
  proxy: {
    server: "http://gw.ludaxproxy.com:7777",
    username: "resi-lx_k3m9q2p7xa",
    password: "YOUR_PASSWORD-country-us",
  },
});
const page = await browser.newPage();
await open(page, "https://api.ludaxproxy.com/whoami");
console.log(await page.textContent("body"));
await browser.close();

These are the messages Playwright gives for each gateway answer (Chrome): net::ERR_TUNNEL_CONNECTION_FAILED for a 502, net::ERR_HTTP_RESPONSE_CODE_FAILURE when the login is refused, net::ERR_PROXY_CONNECTION_FAILED when the gateway cannot be reached at all.

05

Playwright Test: a proxy for the whole test run

End-to-end tests that must see a site from another country. Put the proxy in playwright.config.ts and every test uses it; keep the password in an environment variable.

playwright.config.ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
  use: {
    // every test in the run uses one US IP for 60 minutes
    proxy: {
      server: "http://gw.ludaxproxy.com:7777",
      username: "resi-lx_k3m9q2p7xa",
      password: `${process.env.LUDAX_PASSWORD}-session-ci01-time-60-country-us`,
    },
  },
});

Official docs: Playwright Test: use options

[ 04 / TLS PORTS ]

Do I need the TLS ports with Playwright?

No. The TLS ports exist for HTTP libraries, which give themselves away in the TLS handshake. A real browser does not.

Before a site reads a single header, the client says hello: the TLS handshake. Python’s requests or Node.js fetch say it in a way no browser does, and anti-bot systems block on that. Residential TLS fixes it at the gateway, by connecting with a real browser’s handshake.

Playwright drives a real Chromium, so the site already sees a real Chrome handshake: the whoami answer above shows it as tls_profile. Use the standard gateways, gw.ludaxproxy.com:7777 or Premium gw.ludaxproxy.com:8080: they pass HTTPS through untouched, with no certificate to install. If a site still blocks a headless browser, the cause is the browser’s behaviour or the IP’s reputation, not the handshake: try headed mode, slower pacing, sticky sessions or Residential Premium.

[ 05 / TROUBLESHOOTING ]

Playwright proxy errors and fixes

What Playwright shows, what it means and the one thing to do. All gateway answers: status codes.

SYMPTOM, CAUSE, FIX
You seeWhyFix
net::ERR_TUNNEL_CONNECTION_FAILEDThe gateway answered 502: the exit IP dropped or could not reach the site.Retry (recipe 04). With a sticky session, switch to a new token after two failures. Many at once: check the network status.
net::ERR_HTTP_RESPONSE_CODE_FAILURE or a 407The login was refused: a typo, a prefix that does not match the port, or a plan with no data left.Copy the credentials again from Generate; check the plan. Do not loop: 30 failed logins in 5 minutes block your IP for 10 minutes. Every 407 cause.
net::ERR_PROXY_CONNECTION_FAILEDThe gateway could not be reached: a wrong host or port, or a firewall.Check the server is http://gw.ludaxproxy.com:7777 (Premium gw.ludaxproxy.com:8080) and that your network allows outgoing connections to it.
The IP changes between requests of one pageNo session token: every new connection can get a new IP.Add -session-<token>-time-30 to the password, one token per context (recipe 01).
The proxy works for one context onlyAll contexts share the launch proxy and one token.Set the proxy in newContext(), with a different token for each context.
A SOCKS5 proxy refuses the loginPlaywright passes a username and password to HTTP proxies.Use the HTTP gateway: http://gw.ludaxproxy.com:7777.
A different city than askedNo free exit in that city at that moment: the request falls back to the country.Check where you landed with whoami; the locations directory lists the cities of every country.
[ 06 / FAQ ]

Playwright proxy FAQ

Short answers. The full option list is in proxy authentication.

01How do I use a proxy with a username and password in Playwright?

Pass them in the proxy option: server http://gw.ludaxproxy.com:7777, username resi-lx_k3m9q2p7xa and password YOUR_PASSWORD with your options. It works in launch() for the whole browser or in newContext() for one context, in Node.js, Python, Java and .NET. There is nothing else to install.

02How do I rotate proxies in Playwright?

Give each browser context its own session token in the password (-session-<token>-time-<minutes>). A new token means a new IP; the same token keeps it. Without any token each new connection can leave from a new IP, even within one page.

03Can each Playwright context have a different proxy?

Yes. Set the proxy in browser.newContext() and it applies to that context only. One browser can run many contexts, each with its own IP. It works without a proxy at launch (tested with Playwright 1.64 on Windows).

04Do I need the Residential TLS ports for Playwright?

No. Playwright drives a real browser, which already sends a real browser TLS handshake. Use the standard gateways: gw.ludaxproxy.com:7777 for Residential, gw.ludaxproxy.com:8080 for Residential Premium.

05Does Playwright support SOCKS5 proxies with authentication?

Playwright’s username and password apply to HTTP proxies. Use the Ludax HTTP gateway, which carries HTTPS too, whenever the proxy needs a login.

06Is there a free trial?

No: the smallest plan is the test. 1 GB of Residential costs $0.99, with no subscription, and your username stays the same when you buy more.

[ START NOW ]

Your first Playwright proxy run is two minutes away

  1. Pick a plan. The smallest one is the test: 1 GB, no subscription.
  2. Generate your credentials in the dashboard.
  3. Paste them into the code generator above and run.

Stuck? Send us your launch options and the error on Telegram and we debug it with you. Every plan and price: pricing.