After My Claude Code Account Was Banned, I Fixed My Exit IP
My Claude account was banned a while ago, and Claude Code stopped working with it. Anthropic does not say which rule was triggered, and appeals rarely come with a detailed explanation. After registering again, I started treating the network exit as part of the setup.
The short version: a single, clean residential IP matters more than constantly switching nodes.
Bans rarely have one cause
Common triggers include:
- an IP that jumps between countries or regions
- datacenter IPs, free proxies, or shared exits
- a payment method that does not match the account region
- bursts of requests, or one account shared across devices or people
Anthropic does not explain individual cases, so the practical move is to keep every controllable variable stable.
The setup I use now
- A fixed residential IP: a static proxy from webshare.io, around $1.5 per month. That is enough for one person using Claude Code. Pick a fixed endpoint, not a rotating IP.
- Payment: subscribe to Claude Pro through Google Play on Android. Compared with virtual cards, in-app billing is more stable and easier to keep consistent with the account region.
- A two-hop proxy: in Clash Verge, go through an airport first, then use the webshare HTTP proxy as the exit. TUN must be enabled.
Why two hops
Connecting directly to webshare has two problems: the route from where I am may be unreliable, and my home IP is exposed to the proxy provider. With the airport hop:
local → airport (fastest node) → webshare (fixed residential IP) → Claude
The airport gets you out; webshare provides a clean, fixed exit. The target sees the webshare IP, not the airport or your home connection.
One caveat: the airport can still see destination domains and traffic metadata, so it has to be trusted. This chain fixes exit IP reputation; it is not anonymity.
Clash Verge configuration
-
Create a static proxy in the webshare dashboard and note the host, port, username, and password.

-
In Clash Verge Rev: Subscriptions → Global Extension Script, open it, and paste the script below. It applies globally.

-
Replace the webshare endpoint values at the top of the script, then uncomment the username/password fields in the node.
-
Make sure the airport subscription has a group such as “Auto Select” or “URL Test”.
-
Enable TUN. Claude Code is a CLI program and does not read the system proxy settings; without TUN it may not use the proxy at all.
-
Verify with
curl https://ipinfo.io/json— the IP should be the webshare endpoint.
What the script does
The global override works in six steps:
- Finds the airport's auto-select group to use as the second hop.
- Creates HTTP proxy nodes for the webshare endpoints and sets
dialer-proxyon each one to that group. This is the “airport → webshare” chain. - Adds a
🔒 Clean IPselect group containing the webshare nodes, defaulting to the first one. - Rewrites rules: keeps DIRECT / REJECT / PASS rules, points every other proxy rule at the exit group, and drops the original MATCH / FINAL rules.
- Appends two fallbacks:
GEOIP,CN,DIRECT,no-resolvefor domestic IPs, andMATCH,🔒 Clean IPfor everything else. - Handles name collisions and logs a short diagnostic.
HONOR_PROFILElimits injection to a named profile.
Caveats
- This lowers the probability of a ban; it does not guarantee anything. Account behavior, payment, and usage patterns matter too.
- Keep one account on one exit. Do not switch from the US to Japan and back.
- webshare credentials sit in the script as plain text. Never share a config containing them, and rotate the password if it leaks.
- Do not switch between airport nodes and webshare endpoints constantly; the exit group only selects the first node by default.
- This is a personal setup, not advice on bypassing the terms of service. Anthropic's enforcement rules are the authority.
The full script (credentials removed)
function main(config, profileName) {
/* ======================= User configuration ======================= */
// Names of the airport's auto-select groups, in priority order
var AUTO_CANDIDATES = ["Auto Select", "URL Test", "url-test", "Proxy", "PROXY", "Node Select"];
// webshare endpoints [IP, port]
var WS_ENDPOINTS = [
["YOUR_ENDPOINT_IP", 6000],
["YOUR_ENDPOINT_IP", 6001]
];
var EXIT_GROUP = "🔒 Clean IP"; // exit group name
var WS_NODE_PREFIX = "🇺🇸 Clean IP-"; // prefix for generated nodes
var HONOR_PROFILE = false; // true = only inject for a matching profile
/* ====================== End user configuration ==================== */
config = config || {};
var groups = config["proxy-groups"] = config["proxy-groups"] || [];
var proxies = config.proxies = config.proxies || [];
var rules = config.rules = config.rules || [];
// 0. Optional: filter by subscription name
if (HONOR_PROFILE) {
if (!profileName || String(profileName).indexOf("YOUR_PROFILE_KEYWORD") === -1) {
return config;
}
}
// 1. Find the airport's auto-select group as the second hop
var autoGroupName = null;
for (var a = 0; a < AUTO_CANDIDATES.length; a++) {
var cand = AUTO_CANDIDATES[a];
for (var g = 0; g < groups.length; g++) {
if (groups[g].name === cand) { autoGroupName = cand; break; }
}
if (autoGroupName) break;
}
// 2. Avoid name collisions
var taken = {};
proxies.forEach(function (p) { taken[p && p.name] = true; });
groups.forEach(function (gr) { taken[gr && gr.name] = true; });
function uniq(base) {
var n = base, i = 1;
while (taken[n]) { n = base + "·" + i; i++; }
taken[n] = true;
return n;
}
// 3. Create webshare exit nodes, dialed through the airport's fastest node
var wsNames = [];
WS_ENDPOINTS.forEach(function (ep, idx) {
var rawPort = parseInt(String(ep[1]).replace(/[^0-9]/g, ""), 10);
var node = {
name: uniq(WS_NODE_PREFIX + (idx + 1)),
type: "http",
server: ep[0],
port: rawPort,
// webshare requires authentication: uncomment and fill in your credentials
// username: "YOUR_USERNAME",
// password: "YOUR_PASSWORD",
udp: false
};
if (autoGroupName) { node["dialer-proxy"] = autoGroupName; }
wsNames.push(node.name);
proxies.push(node);
});
// 4. Exit group: select, first node by default
var exitGroupName = uniq(EXIT_GROUP);
groups.push({
name: exitGroupName,
type: "select",
proxies: wsNames
});
// 5. Rewrite rules: keep direct/block, send everything else to the exit group
var DIRECT_TARGETS = { "DIRECT": 1, "REJECT": 1, "REJECT-DROP": 1, "PASS": 1 };
var kept = [];
for (var r = 0; r < rules.length; r++) {
var t = String(rules[r]).trim();
if (!t) continue;
var parts = t.split(",");
var type = String(parts[0] || "").toUpperCase();
if (type === "MATCH" || type === "FINAL") continue;
var last = String(parts[parts.length - 1] || "").trim();
if (DIRECT_TARGETS[last]) { kept.push(t); continue; }
parts[parts.length - 1] = exitGroupName;
kept.push(parts.join(","));
}
kept.push("GEOIP,CN,DIRECT,no-resolve");
kept.push("MATCH," + exitGroupName);
config.rules = kept;
// 6. Diagnostics
try {
if (!autoGroupName) {
console.log("[chain] No auto-select group found; webshare will dial directly. Candidates=" +
AUTO_CANDIDATES.join("/"));
} else {
console.log("[chain] second hop=" + autoGroupName + ", exit group=" + exitGroupName +
", nodes=" + wsNames.length);
}
} catch (e) { /* some engines have no console */ }
return config;
}