This is not a roundup of services — it is a breakdown of a setup that runs in production. We keep Claude Code, two AI support agents and a knowledge-graph container on our own server. All of them talk to the Anthropic and OpenAI APIs, and all of them go out through a proxy for AI that we built ourselves. Below is exactly how it works, what to verify, and where the scheme breaks.

One thing up front: a proxy for AI covers two different jobs, and confusing them is expensive. API access from your code and browser access to the ChatGPT web app are blocked by different mechanisms. The setup that works perfectly for the first job does not work at all for the second — and we measured it.

What is actually blocked: measure, do not guess

Before configuring anything, it pays to look at what the refusal looks like. We hit the endpoints from a server in a restricted region, directly and through our own proxy. These are the real response codes:

EndpointDirectThrough our proxyWhat it means
api.anthropic.com403405 Method Not AllowedProxy works: 405 is the API itself answering a GET where it wants POST
api.openai.com/v1/models403401 Missing bearer authenticationProxy works: the API asks for a key, so the request arrived
chatgpt.com403403 cf-mitigated: challengeProxy did NOT help: Cloudflare bot protection kicked in

Read those rows carefully. 403 direct is a geographic refusal: the request is rejected before any authentication, purely because of where it came from. 405 and 401 through the proxy are successes, even though they look like errors: the API accepted the connection and answered on the merits. That is what a correctly configured proxy for Claude Code or a proxy for OpenAI looks like — not "200 OK", but a sensible application-level error instead of a flat refusal.

The third row is the trap that catches almost every homemade proxy for ChatGPT. The header cf-mitigated: challenge means Cloudflare did not block us by country — it demanded proof that we are a real browser driven by a real person. A datacenter IP from a cheap VPS fails that proof, so the ChatGPT web interface stays out of reach. For the API this is irrelevant; for the browser it is a wall. We will come back to it.

Why not just run a VPN

The usual advice is "turn on a VPN". For development work it fits badly.

  • You need one process tunnelled, not the whole machine. When all traffic goes through the tunnel, so do your database queries, internal services and hosting panel. Some of that breaks against IP allow-lists, the rest just gets slower.
  • A server is not a laptop. On a production box a global VPN means your site starts answering visitors from a foreign address, or stops answering at all. We needed the opposite: the site behaves exactly as before, and only specific tools reach out to AI services.
  • Autostart. Cron jobs and systemd units come up long before any desktop VPN client, and fail silently with a network error.
  • Diagnosis. With a global tunnel you cannot answer "did this request go through the proxy or not" — and without that answer you never catch leaks.

So we built a targeted exit: only the processes that need the proxy know about it. The rest of the system cannot even reach it — we enforce that explicitly, see below.

The whole scheme

Our proxy for AI tools is built from two links. The chain looks like this:

claude / AI agents / memory container
   → privoxy on 127.0.0.1:8118     (local HTTP → SOCKS5 bridge)
   → SOCKS5 on a foreign VPS :3128 (ours, IP allow-list only)
   → api.anthropic.com / api.openai.com

Two links instead of one is not complexity for its own sake. Each has a job:

  • SOCKS5 on a foreign server provides the address outside the restricted region. That is the whole point of the exercise.
  • privoxy on localhost is the bridge. Tools are configured through HTTPS_PROXY and HTTP_PROXY, meaning they expect an HTTP proxy, while our upstream speaks SOCKS5. privoxy accepts the HTTP request and hands it to SOCKS. It also keeps the VPS address out of a dozen config files: when the upstream changes, you edit one line in one place.

What follows is step by step, in the same order we did it.

Step 1. A server outside the restricted region

You need the cheapest VPS available. The requirements are modest: 1 core, 1 GB of RAM, a static IPv4. A SOCKS5 proxy barely consumes resources — it only shuttles bytes. Ours shares its machine with two other services and the load is invisible.

What actually matters when choosing:

  • Location. A European node gives us sub-second latency to the APIs. Check that the provider does not filter traffic to the services you need.
  • A dedicated IP, not shared NAT. A proxy on a shared address inherits someone else's reputation.
  • Payment methods you can actually use. Mundane, but half of the hosting market drops out right here.

We rent ours from Aeza — European locations, hourly billing, so an experiment costs almost nothing to abandon. That is a referral link; the price is the same for you. We deliberately quote no prices: they change faster than articles get updated.

Once the server is provisioned you get an IP and a root password. Everything below runs on that server; addresses in the examples come from the documentation range, so substitute your own:

ssh root@203.0.113.10

Step 2. The SOCKS5 server: dante

We use dante: it speaks SOCKS5 properly, it is stable, and it needs no TLS setup.

apt update && apt install -y dante-server

Now /etc/danted.conf. Here it is in full, with the access logic we run in production:

logoutput: /var/log/danted.log

internal: eth0 port = 3128
external: eth0

method: none
user.privileged: root
user.notprivileged: nobody

client pass {
        from: 203.0.113.50/32 to: 0.0.0.0/0
        log: connect disconnect error
}
client block {
        from: 0.0.0.0/0 to: 0.0.0.0/0
        log: connect error
}

pass {
        from: 203.0.113.50/32 to: 0.0.0.0/0
        protocol: tcp udp
}
block {
        from: 0.0.0.0/0 to: 0.0.0.0/0
        log: connect error
}

Here 203.0.113.50 is the machine that will use the proxy — your server or your home IP. Two parts deserve explanation.

Why method: none is not a hole

method: none means no username and password. That sounds alarming, but the protection here works differently: access is granted strictly by IP, and everything else is block. For a server-to-server setup that beats a password, because a password can leak into git, into a log, or into someone's chat transcript, while an allow-list leaks nowhere.

What you must not do is keep method: none without the block rules. An open SOCKS5 proxy is found by scanners within hours, and from then on your address carries someone else's spam and brute-force traffic — with a predictable letter from your hosting provider. We have seen servers suspended over exactly this, data included.

The trap: two dante versions with incompatible configs

This one cost us an hour. On older distributions — ours runs Ubuntu 16.04 with dante 1.1.19 — the syntax is different from every example on the internet, which is written for the 1.4 branch:

MeaningOld syntax (1.1.x)New (1.4.x)
Authentication methodmethod: nonesocksmethod: none
Pass rulepass { }socks pass { }

A config written for the wrong branch is rejected outright. So always parse it before starting, instead of hoping:

danted -V -f /etc/danted.conf

The command either stays silent or points at the offending line. Only then start the service.

The firewall: the allow-list belongs in two places

Rules inside dante are the first layer, not the only one. Mirror them in the firewall:

iptables -A INPUT -p tcp --dport 3128 -s 203.0.113.50 -j ACCEPT
iptables -A INPUT -p tcp --dport 3128 -j DROP

Why say the same thing twice: a package upgrade can overwrite the daemon config, while the firewall is an independent layer. Ours lives in a script invoked at boot — verify that your rules survive a reboot, otherwise the first restart opens the port to the whole internet and nothing tells you.

Step 3. Autostart that does not survive a reboot

This gets its own section because the failure is silent and surfaces weeks later, during an unplanned restart.

The packaged init script for dante on older systems declares its dependencies in a way that starts the daemon before the network interface has an address. The config says internal: eth0, there is nothing to bind to, and the service dies. Checking this "after a reboot" is too late: the proxy is simply gone and your AI tooling fails without explanation.

We replaced the init script with our own unit at /etc/systemd/system/danted.service:

[Unit]
Description=Dante SOCKS5 server
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/danted.pid
ExecStart=/usr/sbin/danted -D -f /etc/danted.conf
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

The two lines that matter are After=network-online.target (wait for the network) and Restart=on-failure (come back on your own). Enable it, then prove the restart actually works:

systemctl daemon-reload
systemctl enable --now danted
kill -9 $(cat /var/run/danted.pid)
sleep 7; systemctl is-active danted    # expect: active

If the old init script is still enabled, disable it, or two services will fight over the port.

Step 4. The bridge on your side: privoxy

Back on the machine that runs Claude Code:

apt install -y privoxy

Reduce /etc/privoxy/config to exactly two meaningful lines — this is what we run in production:

listen-address  127.0.0.1:8118
forward-socks5t  /  203.0.113.10:3128  .

Both lines carry the whole point of this step.

listen-address 127.0.0.1:8118

Loopback only. Write 0.0.0.0:8118 instead and you have published an open HTTP proxy to the entire internet — the very thing you guarded against one step earlier, now on your own machine.

forward-socks5t — the t is not a typo

This is the most valuable line in the article. There are two similar options: forward-socks5 and forward-socks5t.

With socks5, privoxy resolves the hostname locally and passes a ready-made IP into SOCKS. The connection works and everything looks right — but the list of every service you contact leaves your machine as plain DNS queries to your provider's resolver. The traffic is hidden; the map of where you go is not.

With socks5t the name travels into SOCKS as a name and is resolved on the foreign VPS. No leak.

Prove it with a measurement rather than faith. Check the DNS transaction counter before and after a proxied request:

resolvectl statistics | grep -i 'Total Transactions'
curl -x http://127.0.0.1:8118 -o /dev/null -s https://api.anthropic.com/v1/messages
resolvectl statistics | grep -i 'Total Transactions'

Ours grows by exactly 0 — the local resolver was never touched, the name went down the tunnel. If your counter moves, you are running socks5 without the t. This is easily the most common unnoticed mistake in homemade proxies for AI services.

Step 5. Wiring up Claude Code and the rest

All that is left is telling the tools where the exit is. A proxy for Claude Code is enabled through environment variables — no special config format required.

Claude Code reads the standard variables, so you can test immediately:

HTTPS_PROXY=http://127.0.0.1:8118 HTTP_PROXY=http://127.0.0.1:8118 claude

To avoid typing that every time we added an alias to ~/.bashrc, together with a memory cap, because long sessions can grow and starve neighbouring services:

alias claude='HTTPS_PROXY=http://127.0.0.1:8118 HTTP_PROXY=http://127.0.0.1:8118 systemd-run --scope -q -p MemoryHigh=1800M -p MemoryMax=2500M -p MemorySwapMax=1G claude'

The alternative is an env block in the Claude Code settings file, which works regardless of your shell. Pick one and know which one you use, or you will spend time wondering why the proxy is "sometimes there and sometimes not".

Where an alias will not help

An alias exists only in an interactive shell. Cron jobs, systemd units and containers never see it — a lesson we learned twice. If the AI tool runs on a schedule or as a service, set the variables explicitly:

[Service]
Environment=HTTPS_PROXY=http://127.0.0.1:8118
Environment=HTTP_PROXY=http://127.0.0.1:8118

The mirror image of the same problem: a NO_PROXY=* added to a unit "just in case" silently disables the proxy for everything that unit launches. That is how our service messages stopped being delivered once.

Step 6. Verification: what should answer

The final checklist, confirming that both the proxy for Claude Code and the proxy for OpenAI are live. Run it on the machine with privoxy:

# 1. Exit address — must be the VPS, not you
curl -x http://127.0.0.1:8118 https://api.ipify.org

# 2. Anthropic API — expect 405 (a success, see above)
curl -x http://127.0.0.1:8118 -o /dev/null -w '%{http_code}\n' https://api.anthropic.com/v1/messages

# 3. OpenAI API — expect 401
curl -x http://127.0.0.1:8118 -o /dev/null -w '%{http_code}\n' https://api.openai.com/v1/models

The flag that makes the test meaningless

Never test a proxy with --noproxy '*'. That flag disables the very proxy you passed with -x: the request goes out directly and the result looks like a success. We fell for it while debugging and spent half an hour admiring a tunnel that was not there.

If your shell has inherited proxy variables, strip them explicitly while testing:

env -u HTTP_PROXY -u HTTPS_PROXY curl -x http://127.0.0.1:8118 https://api.ipify.org

Keeping the proxy away from other code

A server that also hosts a website carries a whole class of risk: if someone finds an SSRF or a file upload in your application code, they inherit a ready-made internet exit under someone else's address — your proxy. So we forbid the web user from reaching the privoxy port at all:

-A ufw-before-output -o lo -p tcp --dport 8118 -m owner --uid-owner www-data -j REJECT

The rule must come before the general allow rule for loopback, otherwise it never matches. The effect: the proxy is available to root-owned tools only, and the site code cannot see it. It also rules out the duller scenario where some stray script on the server starts using your tunnel.

How much load this handles

Numbers from our own log, so you have something to compare against. A single SOCKS5 proxy on the cheapest VPS carries:

DateConnections per day
16 September29,395
17 September18,268
18 September26,618

That is Claude Code, two AI agents, a memory container and scheduled synchronisation — roughly 20,000–30,000 connections a day on a single core, with no measurable load. The practical takeaway: for development work there is no reason to buy a bigger server, since something else will become the bottleneck long before the proxy does.

Latency to the APIs through a European VPS is 0.2–0.4 seconds. Next to the time a model spends thinking, that is invisible.

ChatGPT in a browser: why a server does not help

Back to the third row of the first table. Your own SOCKS5 on a VPS completely solves the problem for APIs: Claude Code, the OpenAI API, agents, scripts. Open the ChatGPT web app through it, however, and you get 403 with cf-mitigated: challenge — a Cloudflare check.

So a proxy for ChatGPT in the browser is a separate story, and a cheap VPS does not close it. The reason is not the country but the type of address. Datacenter IPs are visible as server addresses, and bot protection treats them differently from the addresses of real people. Add to that the fact that a VPS address is usually already well-worn, since other customers of the same host use neighbouring IPs.

Browser scenarios need an address that looks residential: a mobile proxy on a carrier network, or a residential one. A mobile address is shared by dozens of live subscribers, which is precisely why it is treated as live. If you need an address nobody else shares, look at elite mobile proxies.

When you need many such addresses continuously, running your own farm becomes cheaper — that is a separate topic: ProxyFarm.

Choosing by use case

Use caseWhat fitsWhy
Claude Code, Anthropic and OpenAI APIs, scriptsYour own SOCKS5 on a foreign VPSAPIs never test for a "real user"; cheap, and handles tens of thousands of connections
ChatGPT web app, one accountMobile or residential proxyA datacenter address triggers the Cloudflare challenge
Several AI service accountsA separate mobile proxy per accountA shared address links the accounts together
Scraping with AI processing, at volumeYour own mobile proxy farmAt scale, renting costs more than hardware

Common mistakes

  • socks5 instead of socks5t — the tunnel works, DNS leaks. Verified by the transaction counter.
  • Testing with --noproxy '*' — disables the proxy under test. The request goes direct and "works".
  • An allow-list in one place only. Daemon config or firewall — you want both layers, because either one alone is easy to lose.
  • A config for the wrong dante branch. socksmethod is rejected by 1.1.x. Always danted -V -f first.
  • Autostart never tested by rebooting. A service that starts before the network dies silently.
  • An alias instead of unit variables. Anything scheduled will not see the proxy.
  • Restarting privoxy mid-flight. It drops every live connection, so a request in progress fails. Do it during a pause.
  • Expecting a 200. For these APIs the correct answer is 401 or 405. Demanding a 200 sends you fixing something that already works.

FAQ

Can I skip privoxy and point tools straight at SOCKS5?

If the tool speaks SOCKS, yes. But most are configured through HTTPS_PROXY, which expects an HTTP proxy, so a bridge ends up simpler than bending every client. It also keeps the upstream address in one file: changing VPS means editing one line, not ten.

How is a proxy for ChatGPT different from a proxy for Claude Code?

In what it demands of the address. A proxy for Claude Code serves an API and only needs an address outside the restricted region — a cheap VPS will do. A proxy for ChatGPT in the browser must also pass a "is this a real user" check, which datacenter IPs fail. If you use ChatGPT through the API rather than the website, there is no difference: the same setup as for Claude Code is enough.

Are free proxies from public lists usable?

Not for AI services. Such an address has been burned by hundreds of users already, and the proxy operator sees all your traffic, including API keys in the headers. An API key is money; do not hand it to an unknown node.

How much latency should I expect?

Through a European VPS we see 0.2–0.4 seconds to the APIs. You only notice it on very frequent small requests; in Claude Code it is imperceptible.

Will a mobile proxy work for APIs?

It will, but you are overpaying: APIs do not check whether an address looks residential. Keep mobile proxies for browser scenarios, where IP reputation decides the outcome.

How do I know the proxy has stopped working?

The symptom is a sudden 403 where things used to work. First thing to check: whether your own machine changed its IP. Our access is granted by allow-list, and a changed address switches the proxy off silently, with no notification.

Can I run other things on the same server?

Yes. Our SOCKS5 box runs other services alongside the proxy without interference, because the proxy consumes almost nothing. Just keep the ports separate and make sure the firewall rules do not overwrite one another.

In short

  • APIs and browsers are different problems. A proxy for AI serving an API and one serving a web interface are built differently: Claude Code and the OpenAI API need only a cheap foreign VPS, while the ChatGPT web app needs a mobile or residential address, or Cloudflare issues a challenge.
  • The correct test result is not 200. A 405 from Anthropic and a 401 from OpenAI both mean the proxy works.
  • The t in socks5t decides whether your DNS leaks. Measure it with the transaction counter: zero growth is the target.
  • Allow-list in two places, and test autostart by rebooting. Open proxies are found within hours, and a service starting before the network dies quietly.
  • Give the proxy only to processes that need it. The web user is denied the port: otherwise an SSRF in your site becomes a ready-made internet exit.

If you need an address that looks like a real user's, take a look at Frigate Proxy mobile proxies, with replacement if anything goes wrong. And for building a personal tunnel for the rest of your traffic, we covered that separately: how to set up your own VPN.