Skip to content

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
Configure the proxy per client. Exporting HTTPS_PROXY in the shell that runs your coding agent would also send the agent’s own model traffic through the proxy.
TrafficThrough netdash?Where it's configured
Claude Code to its model APINoLeave Claude Code's own network settings alone
Your application's HTTP client (requests, httpx, undici, net/http)YesIn code, from NETDASH_USERNAME and NETDASH_PASSWORD
The Playwright MCP browserYesIn a Playwright MCP config file (browser.launchOptions.proxy)
Package installs, git, downloads of MCP serversNoNothing 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.

.env.example
# Copy to .env (gitignored) and fill in your credentials from the dashboard
NETDASH_USERNAME=USERNAME
NETDASH_PASSWORD=PASSWORD
.claude/settings.json
{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(!.env.example)"
    ]
  }
}
Syntax-checked

What these rules do, according to Claude Code's permissions docs:

  • A Read deny rule covers Claude Code's built-in file tools and the file commands it recognizes in the shell, such as cat and head.
  • It also blocks editing or creating that file, so you create .env yourself and type the password there, not into the chat.
  • The ! rule carves .env.example out 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 .env at 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.

Setup prompt for your coding agent
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).

check_proxy.py
"""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())
check-proxy.mjs
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();
}
Checked against a local test proxy

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.

  1. Save the config file outside the repository, for example as ~/.config/netdash/playwright-mcp.json, and make it readable only by you.
  2. 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.
  3. For a team, commit a .mcp.json instead and let each person point to their own config file through NETDASH_PLAYWRIGHT_MCP_CONFIG. Claude Code expands ${VAR} in command and args, and asks each person to approve project servers from .mcp.json before using them.
playwright-mcp.json
{
  "browser": {
    "launchOptions": {
      "proxy": {
        "server": "http://scale-us.netdash.io:2000",
        "username": "USERNAME-cc-US-session-a1b2c3d4-ttl-30",
        "password": "PASSWORD"
      }
    }
  }
}
claude mcp add
# 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"
.mcp.json
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest", "--isolated", "--config", "${NETDASH_PLAYWRIGHT_MCP_CONFIG}"]
    }
  }
}
Syntax-checked

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:

.cursor/mcp.json
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest", "--isolated", "--config", "${env:NETDASH_PLAYWRIGHT_MCP_CONFIG}"]
    }
  }
}
Syntax-checked

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

SymptomCauseFix
Claude Code can't reach its API, or its traffic shows up on your netdash balanceHTTPS_PROXY or HTTP_PROXY holds a netdash URL, in the shell or in the settings env blockUnset it, restart Claude Code and check the Proxy row in /status
Claude Code stops at launch with an error naming a proxy variableThe proxy URL can't be parsed, for example because it has no http:// schemeFix or unset the variable
The MCP browser shows a 407 or a sign-in prompt for the proxyOnly --proxy-server is set, so the browser has no credentialsUse the config file with username and password
The Playwright MCP server from .mcp.json fails to startNETDASH_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 warningSet the variable before starting Claude Code
Cookies or sign-ins carry over between tasksPlaywright MCP uses its persistent profileStart it with --isolated
The exit-IP check works in your terminal but fails when Claude Code runs itClaude 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 connectRun the check yourself, or exclude that command from the sandbox with sandbox.excludedCommands
The agent keeps retrying a challenge pageIts instructions have no stop ruleAdd "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 support

Sources

Third-party behaviour described on this page was checked against these documents.

  1. 01Claude Code docs: Enterprise network configuration (proxy variables, order, startup check, /status)Accessed 2026-10-09
  2. 02Claude Code docs: Environment variables (settings env, subprocess environment)Accessed 2026-10-09
  3. 03Claude Code docs: Settings files and precedence (deny rules for .env)Accessed 2026-10-09
  4. 04Claude Code docs: Configure permissions (Read deny rules, ! carve-outs, limits)Accessed 2026-10-09
  5. 05Claude Code docs: MCP (claude mcp add, scopes, .mcp.json, variable expansion)Accessed 2026-10-09
  6. 06Claude Code docs: Sandboxing (network access for sandboxed commands, excludedCommands)Accessed 2026-10-09
  7. 07Playwright MCP README (--proxy-server, --config, --isolated, configuration file schema)Accessed 2026-10-09
  8. 08Playwright API: browserType.launch(), proxy optionAccessed 2026-10-09
  9. 09Node.js docs: Command-line options, --env-fileAccessed 2026-10-09
  10. 10python-dotenv README (load_dotenv)Accessed 2026-10-09
  11. 11Cursor docs: Model Context Protocol (mcp.json locations, config interpolation)Accessed 2026-10-09

Spotted something out of date? Tell us.

Privacy choices

We use cookies and similar technologies that this site needs to work and to remember your choice. We also use Google Analytics to understand which pages and features people use: in the EEA, the UK and Switzerland only with your consent. Live chat loads only if you open it. You can change your choice at any time under “Privacy choices” in the footer. Privacy Policy

Privacy choices

Choose what netdash.io may use. You can change this at any time under “Privacy choices” in the footer.

Necessary (always on)

Keeps the site working and secure, and remembers your privacy choices (nd_consent, 12 months).

In the EEA, the UK and Switzerland it stays off until you switch it on; elsewhere it is on unless you switch it off here. Google Analytics 4 measures page views, clicks on links and buttons, use of the price calculator, connection builder and code examples, scroll depth, file downloads, and page-performance metrics. It sets the cookies _ga and _ga_96MPNM6GHH, which last up to 12 months. Google processes this data, which may involve transfer to the United States. We do not send your name, email address or account details to Google Analytics. With this switched on we also remember a referral code or campaign you arrived with (nd_attr, 30 days), so a partner can be credited if you sign up.

Support chat (loads only when you open it)

Our live chat is provided by LiveChat. It loads only when you open the chat window. LiveChat then sets its own cookies and processes the messages you send and technical data about your browser.