Use proxies with Claude Code and Playwright MCP
Let Claude Code wire netdash into your app's HTTP clients and give its Playwright MCP browser an authenticated proxy. Don't export HTTPS_PROXY in the shell that runs Claude Code: Claude Code reads it for its own API traffic.
- Updated
- Reading time
- 9 min
- Checked against
- 11 sources
This guide covers proxies for the code and the browser that Claude Code builds and drives: the HTTP clients in your application, and the browser Claude Code controls through Playwright MCP. If you need Claude Code itself to reach the internet through a corporate proxy, that is a different setup, described in Anthropic's network configuration docs. For the architecture and the choice of plan, see proxies for AI agents.
netdash is a standard HTTP proxy with username and password authentication, so there is no SDK or plugin to install. Claude Code only needs to know where the credentials live and which clients should use them.
Which traffic should use the proxy
- Claude Code (or another coding agent)Direct connection, no netdash proxyModel API
- Your application’s HTTP clientnetdash proxy, configured in codeWebsites and APIs you may access
- Playwright MCP browsernetdash proxy, set in the MCP configWebsites the agent may browse
| Traffic | Through netdash? | Where it's configured |
|---|---|---|
| Claude Code to its model API | No | Leave Claude Code's own network settings alone |
| Your application's HTTP client (requests, httpx, undici, net/http) | Yes | In code, from NETDASH_USERNAME and NETDASH_PASSWORD |
| The Playwright MCP browser | Yes | In a Playwright MCP config file (browser.launchOptions.proxy) |
| Package installs, git, downloads of MCP servers | No | Nothing to do, as long as no proxy variable is exported |
Don't export HTTPS_PROXY for Claude Code
Keep netdash out of Claude Code's proxy variables
Claude Code reads HTTPS_PROXY and HTTP_PROXY (and the lowercase forms) for its own traffic, from your shell and from the env block in its settings files. A netdash URL there sends Claude Code's model requests through netdash and meters them against your balance.
Claude Code uses the first of these variables that is set, in the order https_proxy, HTTPS_PROXY, http_proxy, HTTP_PROXY. It reads shell variables once at startup, so a running session doesn't pick up later changes. Commands Claude Code runs and the stdio MCP servers it starts inherit its environment, so every one of them that honors proxy variables would send its traffic through netdash as well.
A global HTTPS_PROXY applies to every request the process makes, including calls to model APIs and package registries. Those requests would go through netdash and count against your balance. Set the proxy on the client that fetches websites instead.
To check, run /status in Claude Code. The Proxy row shows the active proxy URL. If it shows a netdash host, unset the variable or remove it from the settings env block, then restart Claude Code.
Keep credentials in .env
Put the credentials in a .env file that git ignores (add .env to .gitignore), and commit a .env.example with sample values so Claude Code and your teammates know the variable names. Then add deny rules to .claude/settings.json, so Claude Code's file tools don't open .env itself.
# Copy to .env (gitignored) and fill in your credentials from the dashboard
NETDASH_USERNAME=USERNAME
NETDASH_PASSWORD=PASSWORD{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(!.env.example)"
]
}
}What these rules do, according to Claude Code's permissions docs:
- A
Readdeny rule covers Claude Code's built-in file tools and the file commands it recognizes in the shell, such ascatandhead. - It also blocks editing or creating that file, so you create
.envyourself and type the password there, not into the chat. - The
!rule carves.env.exampleout of the rule before it, so Claude Code can still read and update the template. - The rules don't stop a program from reading the file. Your script that loads
.envat run time keeps working, which is what you want. For an operating-system boundary around commands, Claude Code has a sandbox.
Load .env when the program starts instead of exporting the values in the shell that runs Claude Code: run Node.js scripts with node --env-file=.env (Node.js 20.6 and later), and call load_dotenv() from the python-dotenv package at the top of a Python script. Neither overrides a variable that is already set in the environment.
Keep NETDASH_USERNAME and NETDASH_PASSWORD in a .env file or your secret manager, never in source control or in a chat with an assistant.
Setup prompt
Paste this prompt into Claude Code, or into another coding agent, inside your project. It tells the agent to set the proxy on each client instead of exporting HTTPS_PROXY, read the credentials from the environment, set timeouts, report a 407 with a clear error, keep TLS verification on, add a script that prints the exit IP, stop and report when a site challenges or rate-limits the client, and only access sites the project is permitted to use. The prompt links the proxy format reference in Markdown, so the agent can read the exact syntax.
Add netdash proxy support to this project.
Facts (from https://netdash.io/docs/proxy-format.md):
- netdash is a standard HTTP proxy (HTTPS sites go through CONNECT). No SDK.
- Gateways: Solve plan: solve-{us|eu}.netdash.io:1000; Scale plan: scale-{us|eu}.netdash.io:2000; Opus plan: opus-{us|eu}.netdash.io:3000; Mobile: mobile-{us|eu}.netdash.io:4000. Use -us for exits in the Americas, -eu for Europe.
- Username: NETDASH_USERNAME-cc-{COUNTRY} for rotating sessions, or NETDASH_USERNAME-cc-{COUNTRY}-session-{ID}-ttl-{MINUTES} for sticky sessions. COUNTRY is ISO 3166-1 alpha-2 uppercase (GB, not UK). ID is letters and digits only.
- Password: NETDASH_PASSWORD (the proxy password from the dashboard, not the sign-in password).
Requirements:
- Read NETDASH_USERNAME and NETDASH_PASSWORD from the environment (.env, never commit them). Do not hardcode credentials.
- Configure the proxy on the HTTP client or browser explicitly. Do not export HTTP_PROXY/HTTPS_PROXY globally in the shell that runs the coding agent, and do not add them to the coding agent's settings env either.
- Python requests: set both "http" and "https" in proxies. httpx: use proxy=. Node.js: use undici ProxyAgent with undici fetch. Playwright/Puppeteer: pass the server separately from username and password. Selenium/Chrome takes no proxy credentials: use the local forwarder from https://netdash.io/developers/selenium.md.
- Set timeouts, handle HTTP 407 (wrong credentials) with a clear error, and keep TLS verification on.
- Add a small check that prints the exit IP from https://api.ipify.org?format=json through the proxy.
- netdash does not solve CAPTCHAs. If a site shows a challenge or rate-limits the client, stop and report it instead of retrying.
- Only access sites and data the project is permitted to use, respect robots.txt and rate limits.No account or credentials yet? The quickstart covers sign-up, buying a plan and where the dashboard shows your username.
What Claude Code should produce
Whatever the stack, the result should look like the examples in the language guides: the proxy set on one client, the credentials read from the environment, and a check that prints the exit IP. The helpers below are the same ones the Python requests proxy setup and Node.js proxy setup guides use. Ask Claude Code to run the check after it wires the proxy in, with the variables loaded from .env (for example node --env-file=.env check-proxy.mjs).
"""Print the exit IP that netdash assigns, to confirm the proxy setup."""
import os
import sys
from typing import Optional
import requests
NETDASH_USERNAME = os.environ["NETDASH_USERNAME"]
NETDASH_PASSWORD = os.environ["NETDASH_PASSWORD"]
def proxy_url(country: str = "US", session_id: Optional[str] = None,
ttl_minutes: int = 30, gateway: str = "scale-us.netdash.io:2000") -> str:
"""netdash proxy URL: rotating without session_id, sticky with one."""
cc = country.upper()
cc = "GB" if cc == "UK" else cc
username = f"{NETDASH_USERNAME}-cc-{cc}"
if session_id is not None:
username += f"-session-{session_id}-ttl-{ttl_minutes}"
return f"http://{username}:{NETDASH_PASSWORD}@{gateway}"
def main() -> int:
proxy = proxy_url("US")
try:
response = requests.get("https://api.ipify.org?format=json",
proxies={"http": proxy, "https": proxy}, timeout=30)
except requests.exceptions.ProxyError as exc:
if "407" in str(exc):
print("netdash answered 407: check NETDASH_USERNAME, NETDASH_PASSWORD and -cc-", file=sys.stderr)
else:
print("Could not connect through the netdash gateway", file=sys.stderr)
return 1
if response.status_code == 407:
print("netdash answered 407: check NETDASH_USERNAME, NETDASH_PASSWORD and -cc-", file=sys.stderr)
return 1
response.raise_for_status()
print("Exit IP:", response.json()["ip"])
return 0
if __name__ == "__main__":
sys.exit(main())import { ProxyAgent, fetch } from "undici";
const { NETDASH_USERNAME, NETDASH_PASSWORD } = process.env;
export function netdashAgent({ country = "US", sessionId, ttl = 30,
gateway = "http://scale-us.netdash.io:2000" } = {}) {
const cc = country.toUpperCase() === "UK" ? "GB" : country.toUpperCase();
let username = `${NETDASH_USERNAME}-cc-${cc}`;
if (sessionId) username += `-session-${sessionId}-ttl-${ttl}`;
return new ProxyAgent({
uri: gateway,
token: `Basic ${Buffer.from(`${username}:${NETDASH_PASSWORD}`).toString("base64")}`,
});
}
const agent = netdashAgent({ country: "US" });
try {
const res = await fetch("https://api.ipify.org?format=json", {
dispatcher: agent,
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
console.log("Exit IP:", (await res.json()).ip);
} catch (err) {
const detail = String(err.cause?.message ?? err.message);
console.error(detail.includes("407")
? "netdash answered 407: check NETDASH_USERNAME, NETDASH_PASSWORD and -cc-"
: `Request failed: ${detail}`);
process.exitCode = 1;
} finally {
await agent.close();
}Neither script prints the password. The Python script puts the credentials into a proxy URL, so if your password contains characters such as @, : or /, percent-encode it there with urllib.parse.quote(value, safe=""). The Node.js script sends them in a header and needs no encoding.
Claude Code with a Playwright MCP proxy
Playwright MCP, from Microsoft's Playwright project, is an MCP server that gives Claude Code a Playwright browser to drive. Its --proxy-server option takes a server address, and the README documents no username or password option next to it. The credentials therefore go in a Playwright MCP config file. The README documents browser.launchOptions in that file as the options passed to Playwright's browser launch, and Playwright's proxy launch option has username and password fields.
- Save the config file outside the repository, for example as
~/.config/netdash/playwright-mcp.json, and make it readable only by you. - Add the server at local scope with
claude mcp add: it is stored in~/.claude.json, not in the repository. Everything after--is passed to the server unchanged. - For a team, commit a
.mcp.jsoninstead and let each person point to their own config file throughNETDASH_PLAYWRIGHT_MCP_CONFIG. Claude Code expands${VAR}incommandandargs, and asks each person to approve project servers from.mcp.jsonbefore using them.
{
"browser": {
"launchOptions": {
"proxy": {
"server": "http://scale-us.netdash.io:2000",
"username": "USERNAME-cc-US-session-a1b2c3d4-ttl-30",
"password": "PASSWORD"
}
}
}
}# Save the file above outside the repository, readable only by you
chmod 600 ~/.config/netdash/playwright-mcp.json
# Local scope: stored in ~/.claude.json, not in the repository
claude mcp add --scope local playwright -- \
npx @playwright/mcp@latest --isolated --config "$HOME/.config/netdash/playwright-mcp.json"{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest", "--isolated", "--config", "${NETDASH_PLAYWRIGHT_MCP_CONFIG}"]
}
}
}We checked this config file with Playwright MCP 0.0.83 and Chrome 155 against a local test gateway: with --isolated and with a persistent profile, the gateway received the username and password on plain-HTTP requests and on the CONNECT for HTTPS pages. --isolated keeps the browser profile in memory instead of on disk. By default Playwright MCP keeps a persistent profile, so cookies and sign-ins would carry over from one task to the next. Don't combine the config file with --proxy-server: keep the whole proxy definition in one place.
To check the setup, start a new Claude Code session and ask it to open https://api.ipify.org?format=json with the Playwright browser and report the IP address. It should be a netdash exit IP, not your own address.
Sticky sessions for multi-step browser tasks
Playwright MCP launches its browser with the username from the config file. For a task that spans several pages, put a sticky session in that username, as in the example: USERNAME-cc-US-session-a1b2c3d4-ttl-30. Reusing a session ID keeps the same exit IP for the TTL while that IP stays available. The TTL is in minutes, and the dashboard presets are 15 minutes, 30 minutes, 12 hours and 7 days.
For a new session, change the session ID in the file (letters and digits only, no hyphens) and restart the MCP server, for example by restarting Claude Code. For rotating sessions, leave out the session part (USERNAME-cc-US) and the gateway assigns exit IPs. A browser keeps connections open, and requests sent through one HTTPS tunnel share that tunnel's exit IP, so if consecutive pages must share an exit IP, use a sticky session. The sticky session syntax has the details.
Cursor and other coding assistants
The same rule applies to other coding assistants: give the proxy to the clients and the browser they build or drive, not to the assistant's own network settings. This guide doesn't cover an assistant's own proxy configuration.
Cursor reads MCP servers from .cursor/mcp.json in the project, or from ~/.cursor/mcp.json for all projects, and resolves ${env:NAME} in command, args and env. The Playwright MCP entry with the same config file looks like this:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest", "--isolated", "--config", "${env:NETDASH_PLAYWRIGHT_MCP_CONFIG}"]
}
}
}Other MCP clients that the Playwright MCP README lists, such as VS Code and Windsurf, start the same command with the same arguments. The file location and the top-level key differ by client, so check its MCP documentation; Playwright MCP with any MCP client shows the generic configuration. The setup prompt above works with any coding agent.
When a site pushes back
netdash doesn't solve CAPTCHAs, render JavaScript or retry requests: a challenge page reaches the agent's browser exactly as the site sent it. An agent that keeps retrying a challenge only adds load on the site and uses GB. Tell the agent to stop and report instead. The setup prompt includes that rule; add it to your project's CLAUDE.md as well if the agent browses on its own.
netdash forwards your traffic unchanged. It doesn't solve CAPTCHAs, render JavaScript or retry requests. Targeting is country-level.
Use it responsibly
Respect robots.txt and each site's terms, keep request rates reasonable and back off when a site slows you down. Don't collect personal data without a lawful basis or open content behind logins or paywalls without authorization. netdash doesn't solve CAPTCHAs or bot challenges, and your agent shouldn't try to get past them either: if a site rate-limits or challenges your requests, slow down, check its terms, or use its official API. Read how we expect netdash to be used.
Errors and fixes
| Symptom | Cause | Fix |
|---|---|---|
| Claude Code can't reach its API, or its traffic shows up on your netdash balance | HTTPS_PROXY or HTTP_PROXY holds a netdash URL, in the shell or in the settings env block | Unset it, restart Claude Code and check the Proxy row in /status |
| Claude Code stops at launch with an error naming a proxy variable | The proxy URL can't be parsed, for example because it has no http:// scheme | Fix or unset the variable |
| The MCP browser shows a 407 or a sign-in prompt for the proxy | Only --proxy-server is set, so the browser has no credentials | Use the config file with username and password |
The Playwright MCP server from .mcp.json fails to start | NETDASH_PLAYWRIGHT_MCP_CONFIG isn't set where Claude Code runs, so the text ${NETDASH_PLAYWRIGHT_MCP_CONFIG} is passed on unexpanded; claude mcp list shows a missing-variable warning | Set the variable before starting Claude Code |
| Cookies or sign-ins carry over between tasks | Playwright MCP uses its persistent profile | Start it with --isolated |
| The exit-IP check works in your terminal but fails when Claude Code runs it | Claude Code's Bash sandbox is on (it's off by default). Sandboxed commands reach the network only through its own local proxy, so a client that connects to the gateway directly can't connect | Run the check yourself, or exclude that command from the sandbox with sandbox.excludedCommands |
| The agent keeps retrying a challenge page | Its instructions have no stop rule | Add "stop and report on challenges" to the prompt or CLAUDE.md |
Next steps
- Proxies for AI agents
How netdash fits into agent architectures, and which plan and session settings to start with.
- Agent frameworks
Playwright MCP with any MCP client, plus Browser Use, the OpenAI Agents SDK and LangChain.
- Playwright proxy setup
Per-browser and per-context proxies in Playwright for Node.js and Python.
Also useful: cloud browser proxy setup, rotating and sticky sessions, the agent-readable docs index at /llms.txt, and how we expect netdash to be used.
Stuck? See troubleshooting or email support with your username (without the password) and the error message.
Email supportSources
Third-party behaviour described on this page was checked against these documents.
- 01Claude Code docs: Enterprise network configuration (proxy variables, order, startup check, /status)Accessed 2026-10-09
- 02Claude Code docs: Environment variables (settings env, subprocess environment)Accessed 2026-10-09
- 03Claude Code docs: Settings files and precedence (deny rules for .env)Accessed 2026-10-09
- 04Claude Code docs: Configure permissions (Read deny rules, ! carve-outs, limits)Accessed 2026-10-09
- 05Claude Code docs: MCP (claude mcp add, scopes, .mcp.json, variable expansion)Accessed 2026-10-09
- 06Claude Code docs: Sandboxing (network access for sandboxed commands, excludedCommands)Accessed 2026-10-09
- 07Playwright MCP README (--proxy-server, --config, --isolated, configuration file schema)Accessed 2026-10-09
- 08Playwright API: browserType.launch(), proxy optionAccessed 2026-10-09
- 09Node.js docs: Command-line options, --env-fileAccessed 2026-10-09
- 10python-dotenv README (load_dotenv)Accessed 2026-10-09
- 11Cursor docs: Model Context Protocol (mcp.json locations, config interpolation)Accessed 2026-10-09
Spotted something out of date? Tell us.