Proxyca team · 28 Nov, 2025 · Updated 30 Oct, 2026
A reliable proxy check is a short series of repeatable command-line requests, each answering one question: can you reach the proxy, does it accept your credentials, does the request pass through, which address does the destination see, and who resolved the hostname. Run the checks in that order and record the exact output of each, so a failure points to one layer instead of a vague "proxy not working".
This guide is provider-neutral and uses curl. Results depend on your curl version and build, so check what you have installed before you compare notes with anyone else. For endpoint formats, ports and supported protocols, use the documentation of your own proxy provider.
Proxy settings can come from places you forgot about. According to the curl manual, proxy environment variables can be written in lower or upper case, and the lower-case version takes precedence. http_proxy is the exception: it exists only in lower case. The libcurl documentation adds that using a proxy environment variable has the same effect as the --proxy option, and that setting the proxy string to an empty string explicitly disables proxy use even when a variable is set.
So a clean test names the proxy on the command line every time, and a baseline request without a proxy is made deliberately rather than by accident. If a result looks odd, list your proxy-related environment variables first.
The curl manual explains that with no scheme or with http://, the value of --proxy is treated as an HTTP proxy. Prefixes select a specific SOCKS version. Always write the port yourself. The libcurl documentation advises against assuming any single port is widely used for proxies, so do not guess one for an HTTP proxy.
| Scheme | What curl does |
|---|---|
http:// (or no scheme) |
Treats the value as an HTTP proxy |
socks4:// |
Uses SOCKS4 |
socks4a:// |
Uses SOCKS4a; the proxy resolves the hostname |
socks5:// |
Plain SOCKS5; the hostname is resolved locally |
socks5h:// |
SOCKS5; the proxy resolves the hostname |
The resolution notes follow the libcurl documentation, which says socks4a:// and socks5h:// make the proxy resolve the hostname and that socks5:// is plain SOCKS5, and the curl manual, which says --socks5 resolves locally. SOCKS5 is conventionally served on port 1080 according to RFC 1928, and curl assumes 1080 for --socks5-hostname when no port is given, but treat that as a convention and still write the port. For a comparison of the protocol versions themselves, see the article SOCKS4 vs SOCKS5: Authentication, DNS and UDP Compared.
A first connectivity test, with placeholders you replace:
curl --proxy 'http://PROXY_HOST:PROXY_PORT' -U 'USER:PASSWORD' 'http://ECHO_URL'
For an HTTP proxy, credentials go in -U / --proxy-user as user:password. If you embed them in the proxy string instead, curl URL-decodes them, so write @ as %40 and a colon as %3a. A password such as p@ss becomes p%40ss:
curl --proxy 'http://USER:p%40ss@PROXY_HOST:PROXY_PORT' 'http://ECHO_URL'
Basic is curl's default method with proxies. --proxy-anyauth lets curl pick a suitable method for an HTTP proxy, but the manual notes it can cost an extra request/response round trip.
RFC 9110 gives a way to separate two situations:
| Response | What the RFC describes | What to check |
|---|---|---|
407 with Proxy-Authenticate |
A proxy that requires authentication should answer this when credentials are missing, invalid or partial | Username, password, encoding, offered scheme |
| 403 | Valid credentials that are not adequate for access ought to get this | Which component returned it, then access rules |
Every 407 the proxy generates must carry at least one Proxy-Authenticate header, so the offered scheme can be read from it.
A 403 needs one extra step. In forwarding mode, the response may come from the destination rather than the proxy. Before drawing any conclusion, confirm which side produced it. For an HTTPS target, the status line of the CONNECT response comes from the proxy. If the proxy itself returned 403 after accepting your credentials, look at its access rules rather than at a mistyped password. A 407 points at the credentials themselves.
For SOCKS5, authentication happens in two steps. The client sends a method-selection message, and if the server answers with method X'FF', none of the offered methods is acceptable and the client must close the connection. The defined methods are X'00' no authentication, X'01' GSSAPI and X'02' username/password. RFC 1928 says implementations should support username/password, so that method is a recommendation, not a guarantee for every server. In curl, --socks5-basic selects username/password and is enabled by default.
In the username/password step defined in RFC 1929, a status of X'00' means success; any other status is a failure and the server must close the connection. That RFC also states the password travels in cleartext, so the step is not recommended where sniffing is possible and practical. Authentication of this kind controls access to the proxy; it is not encryption of your traffic.
For an http:// target, the client sends the proxy an HTTP request whose target matches the destination URI; this is forwarding mode. For HTTPS targets a CONNECT request asks the proxy to open a tunnel. Per RFC 9110, the CONNECT target is only host:port and there is no default port, so the client must always send one. Any 2xx response means the tunnel is formed and both sides switch to tunnel mode right after the response headers. Any other response means no tunnel exists.
Two practical consequences:
http:// and an https:// URL through the proxy, and record the status the proxy returned for CONNECT. A success on plain HTTP says nothing about tunnelling.The curl manual also notes that operations over an HTTP proxy are converted to HTTP, so some protocol-specific operations may be unavailable unless you tunnel through the proxy. For more background on HTTPS proxies, see What Is an HTTPS Proxy and How Do SSL Proxies Work?.
When a SOCKS5 request fails, the REP field of the reply says why. RFC 1928 defines these among others:
| REP | Meaning | Where to look first |
|---|---|---|
X'00' |
Succeeded | The proxy path works for this request |
X'02' |
Connection not allowed by ruleset | Rules applied by the proxy |
X'03' |
Network unreachable | Network reachability beyond the proxy |
X'04' |
Host unreachable | Reachability of the target host |
X'05' |
Connection refused | The target address and port |
X'07' |
Command not supported | Whether the server implements that command |
X'08' |
Address type not supported | Whether it accepts a hostname or an IP in that form |
The request carries a command (CONNECT X'01', BIND X'02', UDP ASSOCIATE X'03') and an address type (IPv4 X'01', domain name X'03' or IPv6 X'04'), so the client can send either a hostname or an IP. If one form is rejected with X'08', try the other.
A reproducible way to see where a hostname is resolved is to run the same request with both schemes and compare:
curl --proxy 'socks5://PROXY_HOST:PROXY_PORT' -U 'USER:PASSWORD' 'http://ECHO_URL'
curl --proxy 'socks5h://PROXY_HOST:PROXY_PORT' -U 'USER:PASSWORD' 'http://ECHO_URL'
With socks5:// curl resolves the name on your machine; with socks5h:// the proxy does. If only one variant works, name resolution is one possible cause, but not the only one: socks5h:// sends a hostname, so the proxy's handling of that address type can also matter. Check the SOCKS5 reply code, for example X'08' for address type, before concluding. A matching result does not prove that nothing else on your system leaks queries; this test only distinguishes these two curl modes.
Use a current curl for socks5h:// tests. Advisory CVE-2023-38545 concerns hostnames passed to a SOCKS5 proxy for remote resolution, which can be at most 255 bytes, and recommends curl 8.4.0 or later. Check your installed version and build rather than assuming.
Choose an IP-echo endpoint you trust, request it once directly and once through the proxy, and compare the two addresses. That request is the measurement. Do not read the address from the SOCKS reply: in a CONNECT reply, BND.ADDR and BND.PORT describe the address the server assigned for the target connection, and BND.ADDR often differs from the IP you used to reach the SOCKS server, so it is not a stand-in for what a destination sees. Treat the result as an observation about that one request.
X'07'. RFC 8828 notes that HTTP proxies and most SOCKS proxies do not support UDP, so WebRTC falls back to TCP through the proxy. Test UDP-dependent behaviour separately.If the basic checks pass but a particular site still fails, the cause may be a response from the site rather than the connection. The article Why Your Proxy Is Not Working: Errors and Fixes covers separating connection errors, authentication errors and destination responses.
Run both. The first resolves hostnames locally and the second lets the proxy resolve them. Which one suits your application depends on where you need resolution to happen.
Proxies that support CONNECT are advised to restrict it to known ports or a configurable list of safe targets, so a refusal can be policy rather than a malfunction.
Not by the SOCKS5 username/password step: RFC 1929 sends it in cleartext. Whether anything else protects the connection depends on the setup you use.
When your checks give clear results and you want to choose a proxy service, Open Proxyca in Telegram.