Integration · Playwright

Use RoProxy with Playwright

Playwright takes a proxy server, username and password at launch or per browser context, which maps neatly onto RoProxy sessions: every session has its own credentials. This guide covers both setups in Node.js and Python, explains why the HTTP port is the one to use with Chromium, runs several IPs in parallel with one session per context, and picks the session type that keeps a page load consistent.

Use the HTTP port

Chromium does not send a username and password to SOCKS5 proxies: it drops the credentials, the proxy refuses the connection, and the page fails or leaves without the proxy. RoProxy sessions always authenticate, so point Chromium at the session's HTTP port.

HTTPS sites work through that port: the browser opens a CONNECT tunnel and TLS stays between the browser and the site.

One proxy for the whole browser

import { chromium } from "playwright"

const browser = await chromium.launch({
  proxy: {
    server: "http://GATEWAY_HOST:HTTP_PORT",
    username: "USERNAME",
    password: "PASSWORD",
  },
})
const page = await browser.newPage()
await page.goto("https://api.ipify.org?format=json")
console.log(await page.textContent("body"))
await browser.close()

One session per context for parallel IPs

A browser context is an isolated profile with its own cookies. Give each context its own session and one browser runs several IPs side by side.

import { chromium } from "playwright"

// Each entry is one RoProxy session, copied from the dashboard.
const sessions = [
  { server: "http://GATEWAY_HOST:HTTP_PORT", username: "USERNAME_1", password: "PASSWORD_1" },
  { server: "http://GATEWAY_HOST:HTTP_PORT", username: "USERNAME_2", password: "PASSWORD_2" },
]

const browser = await chromium.launch()
await Promise.all(sessions.map(async (proxy) => {
  const context = await browser.newContext({ proxy })
  const page = await context.newPage()
  await page.goto("https://api.ipify.org?format=json")
  console.log(await page.textContent("body"))
  await context.close()
}))
await browser.close()

Choosing the session type for a browser

  • Sticky (bind)

    The default choice for browsers: logins, carts and multi-step flows keep one IP and one cookie jar.

  • Timed rotation

    Good for crawling many pages in a row, as long as no login spans the moment the IP changes.

  • Per request (Business)

    A page loads dozens of resources and each could leave from a different IP, which some sites flag. Better for API-style fetches than for full page loads.

Common errors

  • net::ERR_TUNNEL_CONNECTION_FAILED

    The gateway host or port is wrong, or the session has expired. Check the connection string in the dashboard.

  • 407 or a proxy login prompt

    The username or password is missing or wrong. Pass both in the proxy option, not in the server URL.

  • Slow page loads

    Every resource goes through the proxy. Block images, fonts and media with page.route when you only need the HTML.

Frequently asked questions

Why does the SOCKS5 port not work in Chromium?

Chromium does not send a username and password to SOCKS5 proxies. Use the session's HTTP port; HTTPS sites still work through it.

Can each browser context use a different IP?

Yes. Pass each context its own session's credentials in newContext({ proxy }); every session has its own IP.

Should I use per-request rotation with a browser?

Usually not. A page load pulls many resources, and each could leave from a different IP. Sticky or timed sessions keep a page load consistent.

Can I change the IP without restarting the browser?

Yes. Call the rotate endpoint of the RoProxy API for that session; new connections then leave from the new IP, so open a fresh context to drop old ones.

Related pages

Try it on a free session

Verified accounts get free sessions with random global IPs. No card needed.

Create free account