How to test a proxy: six checks, in order, with commands you can paste
Guides · Published November 2, 2026 · 13 min read
To test a proxy, ask an IP echo service who you are twice, once directly and once through the proxy, and compare the two answers. If the addresses differ, your request left through the proxy. That's the first of six checks, and it's where most guides stop. The other five catch what it can't: the wrong protocol, a country you didn't order, leaks around the proxy, slow or erratic connections, and a target site that rejects the address anyway.
Short version: run curl -s https://api.ipify.org with and without -x http://LOGIN:PASSWORD@proxy.tierproxy.com:823. Two different addresses mean the proxy is in the path. Then confirm the protocol and country, look for DNS, WebRTC, header and IPv6 leaks, time ten requests instead of one, and finish with a small run against the site you actually need.
Six checks tick off in order: exit IP, protocol, location, leaks, speed and real target, followed by a note that all six passing still doesn't mean a site will accept you.
- Compare your direct IP with the IP an echo service sees through the proxy.
- Confirm the protocol and the login with
curl -x, and learn what a 407 or a reset means. - Look the exit IP up in more than one GeoIP source and compare countries first.
- Check four routes around the proxy: DNS, WebRTC, request headers and IPv6.
- Time ten requests with
curl -winstead of trusting one. - Send 20 or more real requests to your target and read the status codes and the bodies.
Before you start
Every command here uses LOGIN, PASSWORD and the gateway proxy.tierproxy.com, with port 823 for HTTP and 824 for SOCKS5. Copy the login and both ports from your own order in the dashboard. If you've picked a country or a sticky session, the dashboard builds the username for you in the form LOGIN__cr.us;sessid.abc123. Here cr.us is the country, optional state., city., zip. and asn. filters can follow it, and sessid.abc123 pins one session. Special characters in a password must be URL-encoded when they sit inside a proxy URL, so @ becomes %40.
Use curl for most of this, because it prints errors that name what failed.
Testing uses bandwidth, and on a per-GB plan that's money. Echo endpoints return a few bytes, so checks 1 to 5 are close to free. Check 6 is where it adds up, because you'll download real pages. For how that traffic gets counted, see the residential bandwidth guide.
Check 1: is traffic really leaving through the proxy?
Ask the same question from two places. api.ipify.org answers a plain GET with your address as bare text, which makes it easy to compare by eye or in a script. Run the direct request first, with no proxy, then the proxied one.
$ curl -s https://api.ipify.org
198.51.100.7
$ curl -s -x http://LOGIN:PASSWORD@proxy.tierproxy.com:823 https://api.ipify.org
203.0.113.45Sample output. The addresses come from the documentation ranges in RFC 5737, so yours will differ.
Your machine asks an echo endpoint directly and gets IP A, then asks again through a proxy and gets IP B with a country label, then the two are compared and found different.
Different addresses: the request left through the proxy. The same address twice: it didn't, and the cause is usually on your side. Tools disagree about who wins when a flag and an environment variable both name a proxy. curl's manual says --proxy overrides the environment, and --proxy "" switches an environment proxy off. Requests warns that values in session.proxies can be overwritten by environment proxies, so pass proxies= on every call.
Then run the proxied command three times. On the rotating gateway you should see a new address on each request. With a sticky username, the sessid. part, you should see the same one every time, and the dashboard says a sticky entry holds its IP for about 30 minutes.
You may prefer https://httpbin.org/ip. It returns JSON, {"origin": "..."}, and origin can hold two comma-separated addresses when the request carries an X-Forwarded-For header. Add -H "X-Forwarded-For: 198.51.100.7" yourself and you'll see it echoed ahead of the real address. Two addresses through a proxy means the proxy forwarded yours.
Our own /my-ip page does the same job in a browser. It reports the address your connection arrived from, so open it in a browser set to use the proxy and you get the exit IP, an approximate location, the network name and the ASN. It also lists your browser's timezone and language, which don't change when the proxy does.
Check 2: does it speak the protocol you think?
A login that works over HTTP can fail over SOCKS5 because of the wrong port or the wrong scheme, so test both rather than assuming. -x takes the protocol from the URL. No scheme or http:// means an HTTP proxy, socks5:// and socks5h:// select SOCKS5, and if you leave the port off, curl assumes 1080. That's rarely what you want.
# HTTP proxy, login inside the URL
curl -s -x http://LOGIN:PASSWORD@proxy.tierproxy.com:823 https://api.ipify.org
# the same, with the login passed separately
curl -s -x http://proxy.tierproxy.com:823 --proxy-user LOGIN:PASSWORD https://api.ipify.org
# SOCKS5, with the proxy doing the DNS lookups
curl -s -x socks5h://LOGIN:PASSWORD@proxy.tierproxy.com:824 https://api.ipify.orgHTTP goes to port 823 and SOCKS5 to port 824. Read both ports from your own order, since that's where they're shown.
The h in socks5h matters more than it looks. With socks5://, curl resolves the hostname on your machine and hands the proxy an address. With socks5h://, the proxy receives the name and resolves it. Requests documents the same split.
A 407 is the proxy's own authentication challenge, the proxy-side sibling of 401. MDN describes it as a response carrying a Proxy-Authenticate header, after which the client may retry with Proxy-Authorization.
| What curl prints | What it usually means | What to try |
|---|---|---|
CONNECT tunnel failed, response 407 | The proxy is reachable and wants a valid login. You sent none, or the wrong one. | Re-check LOGIN and PASSWORD, and encode special characters. |
A 407 status line on a plain http:// URL, with exit code 0 | The same problem, but curl counts a received response as success, so a script thinks it worked. | Print %{response_code} with -w and test it. |
User was rejected by the SOCKS5 server, exit 97 | SOCKS5 is answering and refused the login. Curl's manual lists 97 as a proxy handshake error. | Check the credentials, then the SOCKS5 port. |
Failed to connect ... over proxy, exit 7 | Nothing accepted the connection at that host and port, or a firewall dropped it. | Check the host, the port and your outbound firewall. |
Recv failure: Connection was reset, exit 56 | Something accepted the connection and then cut it. Common causes are a wrong scheme for that port, or a proxy that drops clients it doesn't accept. | Try the other scheme or the other port. |
Connection timed out, exit 28 | Packets are going nowhere: a wrong host, a filtered port, or a limit like --max-time that's too short. | Raise the limit once, then check the host and port. |
Wording and exit codes come from curl 8.23.0. Treat the exit code as a hint and read the message.
Check 3: is the location what you ordered?
Take the exit IP from check 1 and look it up. Then expect some disagreement, because IP geolocation is an inference, not a measurement. MaxMind's accuracy page calls it approximate, says country-level accuracy is generally above 99%, and adds the qualifier "especially when excluding VPN usage".
Databases also disagree with each other. A 2010 comparison by Shavitt and Zilberman found strong correlation overall, but also errors of thousands of kilometres in some cases, and concluded that the accuracy of a returned location can't be trusted. The paper is old, so don't read it as a current error rate. What still holds is the mechanism: each database makes its own guess from its own sources.
So don't fail a proxy over a city. Run the exit IP through two or three lookups, /my-ip among them, and compare countries first. If every lookup agrees on the wrong country, that's a real problem and worth a message through the contact page. If they split on region or city, that's normal, and the database your target uses is the only opinion that counts.
Read the network name too. On a residential plan it should look like an access provider, and on a datacenter plan you'd expect a hosting company. If you added state., city., zip. or asn. filters, add them one at a time so you know which one changed the result.
Check 4: leaks around the proxy
A proxy covers only the traffic that's sent to it. Anything that takes another route shows your real address or your real habits. Four routes come up again and again, and the first one is silent.
DNS
With socks5://, your machine looks up every hostname itself, so your resolver sees where you're going even though the connection goes through the proxy. You can watch this happen: curl -x socks5://LOGIN:PASSWORD@proxy.tierproxy.com:824 http://does-not-exist.invalid fails with Could not resolve host and exit code 6, which means your side did the lookup. Switch to socks5h:// and curl no longer stops at your resolver. It talks to the proxy, and any error comes from there. An HTTP proxy normally gets the hostname in the request, so it does the lookup. In Python, use socks5h in the proxy URL too.
WebRTC
This one only exists in browsers, so curl can't test it. RFC 8828 lists the exact case as a problem: WebRTC's STUN checks bypass the proxy and reveal the client's public address when direct Internet access is permitted. The same RFC notes that HTTP proxies and most SOCKS proxies don't carry UDP, which is why its force-proxy mode falls back to TCP. What your browser does by default varies by browser and version. Open a WebRTC test page in the proxied browser and check that no public address in the candidate list is your own.
Headers the destination can see
A proxy that handles plain HTTP can add headers. X-Forwarded-For carries the originating client address, and MDN calls it a de-facto standard that exposes privacy-sensitive information by design. Via, per MDN, is added by proxies to track forwards. To see what arrives, request http://httpbin.org/headers through the proxy and read the list. Use plain http:// for this. Over HTTPS the proxy relays an encrypted stream and can't edit the request inside it.
IPv6
Dual-stack clients try IPv6 first. RFC 8305 describes starting the IPv6 connection attempt first and giving IPv6 preference. So if your machine has a working IPv6 address and anything bypasses the proxy, such as a tool that ignores proxy settings, a NO_PROXY match or an IPv6 literal in a URL, that traffic leaves from your real IPv6 address. Run curl -s -6 https://api64.ipify.org with no proxy. If it prints an address, you have IPv6, and that address must never show up in any proxied result. If it errors, you probably have nothing to leak.
Check 5: speed and latency
Ask curl to split a request into phases and it will. Five variables cover it, and all of them count from the start of the request, so each one includes the ones above it. The descriptions below paraphrase the curl manual.
time_namelookup: until name resolving finished.time_connect: until the TCP connection to the remote host, or to the proxy, finished.time_appconnect: until the TLS handshake with the remote host finished.time_starttransfer: until the first byte came back.time_total: the whole operation.
for i in 1 2 3 4 5 6 7 8 9 10; do
curl -s -o /dev/null -m 20 \
-x http://LOGIN:PASSWORD@proxy.tierproxy.com:823 \
-w '%{response_code} dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
https://api.ipify.org
doneTen requests, one line each. `-o /dev/null` discards the body, `-m 20` gives up after 20 seconds.
Through a proxy, dns and tcp describe your hop to the proxy, because that's the only host curl resolves and connects to. Everything after that, the proxy reaching the destination and the TLS handshake, lands in the jump from tcp to tls.
One sample tells you almost nothing. On a rotating gateway every request can use a different exit, so every request can take a different path. Say your ten totals are 0.9, 1.0, 1.0, 1.1, 1.1, 1.2, 1.2, 1.3, 1.4 and 6.8 seconds. The mean is 1.7, the median is 1.15, and the mean describes none of the requests. Report the median and the slowest instead. We haven't measured typical numbers for any network, ours included, so judge yours against what your own task needs.
If you'd rather script it, this does checks 1 and 5 together and flags a leak when the proxied address equals the direct one. Without a timeout, Requests can hang for minutes, so it sets one: 5 seconds to connect, 15 to read. For an HTTP proxy it needs only python -m pip install requests. For SOCKS5, install requests[socks] instead.
import time
import requests
PROXY = "http://LOGIN:PASSWORD@proxy.tierproxy.com:823"
ECHO = "https://api.ipify.org"
def fetch(session, **kwargs):
start = time.perf_counter()
ip = session.get(ECHO, timeout=(5, 15), **kwargs).text.strip()
return ip, time.perf_counter() - start
direct = requests.Session()
direct.trust_env = False # ignore HTTP_PROXY and friends for the direct run
proxied = requests.Session()
proxied.trust_env = False
ip_a, t_a = fetch(direct)
print(f"direct {ip_a:<16} {t_a:.2f}s")
for i in range(5):
ip_b, t_b = fetch(proxied, proxies={"http": PROXY, "https": PROXY})
print(f"proxied {ip_b:<16} {t_b:.2f}s {'LEAK' if ip_b == ip_a else 'ok'}")Run it with the placeholders untouched and the gateway answers with a 407. That proves it's reachable and only the login is missing.
Check 6: a small run against your real target
Everything so far used an echo service that doesn't care who you are. Your target does. So finish with a small batch of the exact requests you plan to make, at the pace you plan to make them, and record what comes back. Keep it modest, and follow the site's terms, its robots.txt and the law that applies to you.
PROXY=http://LOGIN:PASSWORD@proxy.tierproxy.com:823
URL=https://example.com/your/real/page
for i in $(seq 1 20); do
curl -s -o "page-$i.html" -m 30 -x "$PROXY" \
-w '%{response_code} %{size_download} %{time_total}\n' "$URL"
done | awk '{c[$1]++; b+=$2} END {for (k in c) print k, c[k]; print "bytes", b}'Prints how many responses came back with each status code, then the total body bytes. The pages are saved so you can open them.
Count a request as a success only if the status is right and the body is the page you wanted. A 403 or a 429 is the site telling you plainly. A 407 is your login. The sneaky result is a 200 whose body is a CAPTCHA, an unusual-traffic notice or an empty shell, so open a few of the saved files and compare their size with the same page fetched directly. A page that weighs 120 KB direct and 6 KB through the proxy is a block page, whatever the status says.
Don't rely on the status code to spot a challenge. Cloudflare's documentation says a challenge response carries a cf-mitigated: challenge header and has a content type of text/html whatever you requested, so a script expecting JSON gets HTML back. To look for that header, add -D - to the curl call, which dumps the response headers to the terminal, and search for it. Other sites use other markers, so read the body.
Twenty requests give a rough answer, not a precise one. If 17 succeed, that's 85%, but the 95% Wilson interval for 17 out of 20 runs from about 64% to 95%, so the true rate could be a good deal worse than it looks. Forty-two out of 50 narrows it to about 71% to 92%.
This is also where the bandwidth shows up. If a page weighs 2 MB, 100 requests move about 200 MB, roughly a fifth of a gigabyte, which is about 24 cents at the $1.20 per GB residential price. The size_download figure counts body bytes only, so the real total runs a little higher.
What /proxy-checker and /my-ip can and can't tell you
/proxy-checker takes up to 5 proxies per run, one per line, as IP:PORT or IP:PORT:USER:PASS. For each one it tries HTTP first and falls back to SOCKS5, fetching a plain-HTTP echo page through the proxy with a 10 second limit. The table reports valid or invalid, the protocol it detected, a response time and an anonymity label.
It runs on our server, not on your machine, and that shapes what it's good for. It answers one question well: can this proxy be reached, and does it respond. It can't see your browser's WebRTC or DNS behaviour, and the results table has no country column. The response time is the whole round trip, including the failed HTTP attempt when a proxy turns out to be SOCKS5, so don't compare it with your curl numbers. The anonymity label comes from comparing the address the echo page saw with the one you typed. It doesn't read headers. A failure mostly reads unreachable, so use curl to find out why.
Two limits matter for a gateway. The checker only accepts IP addresses as the host, so proxy.tierproxy.com:823 comes back as "Invalid host or port", and private ranges are refused the same way. There's also a per-minute limit and a daily one per visitor, currently 40 proxies a day, checked on 2 November 2026.
/my-ip has the opposite shape: no input, one answer. It reports the address your connection reached our server from, with location, ISP, organization, ASN and timezone from a GeoIP lookup, plus browser details such as language and timezone read from the browser itself. It can't be fooled by an X-Forwarded-For header you send, and it can't show you one either. It says nothing about DNS or WebRTC, and it labels its own location as approximate.
What should stop you, and what shouldn't
Stop and fix things when check 1 shows your own address, when check 2 ends in a 407 or a rejected SOCKS5 login you can't explain, or when every GeoIP lookup agrees on the wrong country. Look closer, but keep going, for a city mismatch, one slow sample or a header you don't recognise. A failed check 6 says something about this proxy and this target together. The fix may be slower pacing, a sticky session or different targeting rather than a different proxy.
If you're about to buy, run checks 1 to 5 on a small residential balance first. GB never expires, so nothing is wasted by testing.
Frequently asked questions
How do I check if my proxy is working?
Request an IP echo endpoint such as https://api.ipify.org directly, then again through the proxy with curl -x. If the second address differs from the first, the request went through the proxy. A 407 or a rejected SOCKS5 login means the proxy answered but refused your credentials.
How can I tell if my proxy is leaking my real IP?
Check four routes. Use socks5h:// so DNS goes through the proxy, request http://httpbin.org/headers to look for X-Forwarded-For and Via, check IPv6 with curl -s -6 https://api64.ipify.org, and look at a WebRTC test page in the proxied browser. curl can't test WebRTC, so use the browser you'll really run.
Why does my proxy IP show a different city than I ordered?
GeoIP databases infer location, and they disagree with each other more at city level than at country level. MaxMind describes its own results as approximate. Check the country across two or three lookups before you worry about the city.
What does a 407 mean when I test a proxy?
The proxy is reachable but wants valid credentials, and the response carries a Proxy-Authenticate header. Re-check the username and password. If the password has special characters, URL-encode them inside the proxy URL, for example @ as %40.
How many times should I test a proxy's speed?
Send at least ten requests and report the median and the slowest. One sample hides variation, and on a rotating gateway each request can use a different exit and a different path.
Will a proxy that passes these checks work on every website?
No. The checks show that the proxy routes your traffic, speaks the protocol and isn't leaking. A site can still block or challenge the address. Check 6 samples that for your target, and the answer can change over time.
Sources
- curl manual: --proxy, --write-out and exit codes
- Requests documentation: proxies, SOCKS and timeouts
- RFC 8828: WebRTC IP Address Handling Requirements
- RFC 8305: Happy Eyeballs Version 2
- MDN: X-Forwarded-For header
- MDN: Via header
- MDN: 407 Proxy Authentication Required
- MaxMind: IP geolocation accuracy
- Shavitt and Zilberman: A Study of Geolocation Databases (2010)
- Cloudflare docs: detecting a challenge page response
Keep reading
- HTTP vs SOCKS5 proxy: what changes and which to use
- How much proxy bandwidth do you need? Pages per GB and cost
- Rotating vs sticky proxies: how proxy sessions work