Skip to content

Guides · Connect and debug

How to test a proxy with curl

Before connecting a job to a proxy, check that the client reaches the gateway, authenticates and receives a response from a small permitted endpoint. Record the address the endpoint sees, then repeat the check in the application that will do the work.

Short answers

How do I check if my proxy is working?

Send one request through it to an endpoint that reports the address it sees, with curl -x for the proxy. For an interactive check, give --proxy-user the full username only and enter the password at the prompt. Confirm the expected response status and body; an error response is not a successful destination check. Record the curl error separately from any HTTP status.

How do I see which country my proxy exits from?

Ask a service that echoes the address it sees; ours answers on the echo endpoint printed in the setup docs. It returns the exit IP, not a country: look that address up in the geolocation database your target site uses, when that source is available. A database classification may still differ from the location the website reports. The API connection test makes a separate request from our side. It returns exit_country only for an observed classification, with country_verified true. Otherwise exit_country is null and country_verified is false. This is not proof of a physical location.

How do I test a SOCKS5 proxy with curl?

Use the same -x flag with a socks5h:// URL and port 7001 instead of http:// and port 7000. The h in socks5h tells curl to resolve the hostname at the proxy rather than locally, so choose it when name resolution should happen at the proxy. Local and remote DNS can return different destination addresses.

Why does curl succeed while my browser fails?

The browser can use different proxy settings, credentials or exclusions. In Playwright, configure the server, username and password in the browser proxy options, then test a page navigation. The client settings guide covers the comparison.

How do I confirm that rotation actually changed the IP?

Compare the reported address across a few permitted connections using the same configuration. Two rotating connections can use the same public address. A repeated address alone does not prove rotation failed, and an IP comparison does not identify the underlying device.

Does testing use my GB?

Yes. Traffic sent through the gateway uses the account balance, including the diagnostic request and response. Keep tests small and bounded. The account meter refreshes periodically, so it may not change immediately after a test.

How do I measure proxy latency?

Use curl’s time_connect, time_starttransfer and time_total fields. time_connect ends when TCP reaches the host or proxy; time_starttransfer ends at the first response byte. Their difference includes several stages, not just destination processing.

Calling cURL from PHP? Use the PHP cURL guide for the extension’s options, CONNECT status and a prompted-password example. Shell flags below are not PHP option names.

The one-line check

The proxy address goes in -x, the full username goes in --proxy-user without a colon or password, and the destination is an endpoint that reports the address it sees. In an interactive terminal, curl asks for the password. Enter it at that prompt; each curl invocation asks again. Keeping the password out of the URL also avoids mixing credential text with URL syntax. The 407 guide covers authentication failures. curl interactive authentication.

USERNAME stands for the account part of your username; both it and the pak_ password are in the dashboard and in GET /v1/traffic.

Connect, authenticate, read the exitsh
# Replace USERNAME with the account part shown in the dashboard.
# Enter the password at each curl prompt; leave it out of the command.

curl --disable --silent --show-error --noproxy '' --connect-timeout 5 --max-time 15 -x "http://gw.portproof.org:7000" \
  --proxy-user "USERNAME-mbl-us-rot-auto10" \
  https://api.portproof.org/v1/echo-ip

# record tunnel and destination statuses separately
curl --disable --silent --show-error --noproxy '' --connect-timeout 5 --max-time 15 -o /dev/null -w 'connect=%{http_connect} target=%{response_code}\n' \
  -x "http://gw.portproof.org:7000" \
  --proxy-user "USERNAME-mbl-us-rot-auto10" \
  https://api.portproof.org/v1/echo-ip
These examples require an interactive terminal. The transfer timeout does not set a deadline for entering the password. The prompt keeps the password out of the command you type; it does not protect every place credentials are used or stored. For unattended jobs, use your existing secret mechanism and the setup documentation. Expanding a password environment variable into a command option still puts its value in a process argument, where local users may see it. Do not enable shell tracing or share credential-bearing traces. curl credential guidance.

Exit address and round trip

The echo endpoint reports the exit IP it received. It does not confirm the exit country; a geolocation lookup is a separate observation and different services can classify the same address differently. time_connect measures time until the TCP connection to the host or proxy is complete. time_starttransfer measures time until the first response byte. Their difference includes more than destination processing, so it is not a direct measure of the target site’s work.

Keep the endpoint, client and settings consistent when comparing timings. Record several bounded checks; one result is only one observation.

Timing breakdownsh
curl --disable --silent --show-error --noproxy '' --connect-timeout 5 --max-time 15 -o /dev/null \
  -w 'connect %{time_connect}s  first byte %{time_starttransfer}s  total %{time_total}s\n' \
  -x "http://gw.portproof.org:7000" \
  --proxy-user "USERNAME-peer-gb-rot-auto10" \
  https://api.portproof.org/v1/echo-ip

The API can also run the check from our side: POST /v1/traffic/test connects with a pool, a country and a rotation mode and reports whether it worked, the exit IP and the latency. It is limited to six calls a minute. Read country_verified: only true marks an observed country classification in exit_country. Otherwise exit_country is null. The requested country is never returned as the exit, and an observed classification is not physical-location proof. This separate check does not prove where an earlier client request exited or how a website classifies it.

Observe per-connection rotation

Per-connection rotation selects a route for each new connection. Two rotating connections can use the same public address. Compare the configuration and the result of several permitted checks; a repeated address alone does not prove rotation failed. The example addresses below are masked documentation-range values, not pool exits.

Example output only. Addresses in 203.0.113.0/24 and 198.51.100.0/24 are documentation ranges, not exits of the pool.

Compare two connection resultssh
for i in 1 2; do
  curl --disable --silent --show-error --noproxy '' --connect-timeout 5 --max-time 15 -x "http://gw.portproof.org:7000" \
    --proxy-user "USERNAME-peer-es-rot-ondemand" \
    https://api.portproof.org/v1/echo-ip
  echo
done

# example: the exits differ
#   203.0.113.x
#   198.51.100.x

Timed mode requests rotation on the selected schedule. It does not promise a different public address at an exact instant or change every request on an already open connection. The rotation and duration guide explains the boundaries.

Observe a sticky session

Repeat a check with one session namesh
U="USERNAME-mbl-gb-sid-check7-rot-sticky"

curl --disable --silent --show-error --noproxy '' --connect-timeout 5 --max-time 15 -x "http://gw.portproof.org:7000" --proxy-user "$U" https://api.portproof.org/v1/echo-ip; echo
sleep 5
curl --disable --silent --show-error --noproxy '' --connect-timeout 5 --max-time 15 -x "http://gw.portproof.org:7000" --proxy-user "$U" https://api.portproof.org/v1/echo-ip; echo

# example: both calls report the same exit
#   192.0.2.x
#   192.0.2.x
A sticky session keeps the same device; the carrier may still change the IP. Equal addresses do not prove device identity. If an unchanged IP is a requirement of your test, pause on a change and inspect the settings and errors. Use the sticky-session troubleshooting guide for the next checks.

The same checks over SOCKS5

socks5h resolves the hostname at the gateway; plain socks5 resolves it locally using the client’s resolver.

SOCKS5 with remote DNSsh
curl --disable --silent --show-error --noproxy '' --connect-timeout 5 --max-time 15 -x "socks5h://gw.portproof.org:7001" \
  --proxy-user "USERNAME-peer-nl-rot-ondemand" \
  https://api.portproof.org/v1/echo-ip

Which protocol to prefer, and what SOCKS5 can carry that an HTTP proxy cannot, is in the HTTP or SOCKS5 guide.

What each failure message means

Common curl failures against a proxy and their cause
What you seeWhere it failedUsual cause
HTTP 407at the gatewayRejected account credentials or exhausted traffic. Read the gateway code; malformed username syntax is documented as 400 E_USERNAME_PARSE.
HTTP 400 with E_USERNAME_PARSEat the gatewayUpper case, a hyphen inside a value, or an unknown token in the username.
HTTP 502 with E_NO_STOCK_COUNTRYat the gatewayNo device of that pool is online in that country right now.
Connection refusedbefore the gatewayWrong port, or a local firewall. HTTP is 7000, SOCKS5 is 7001.
Operation timed outconnection or transferThe configured deadline elapsed. Check the failing stage and endpoint before changing a timeout.
SSL certificate problemat the destinationYour own trust store, or a network that intercepts TLS. The proxy tunnels TLS; it does not terminate it.
Empty reply from servertransferNo HTTP response was received. Inspect the connection and server evidence before assigning a cause.

For a refused connection, a timeout or a gateway name that does not resolve, follow connection refused and timeouts. For a failed tunnel, diagnose the CONNECT response separately from the destination status.

The same test in Python and Node

Once curl succeeds, repeat the check from the process that will run your job. The complete Python Requests setup covers encoded credentials, timeouts and SOCKS5; the Node.js fetch setup covers explicit dispatchers, deadlines and response cleanup. For a PowerShell script, use the PowerShell proxy guide to set the gateway and prompted credential on one request.

What a test costs

Proxy tests use traffic. The response body is only part of a transfer, so do not estimate the charge from its size alone. Keep the endpoint small, limit repeated checks and compare a representative run with the account usage meter after it refreshes.

The GB planning guide helps size a workload, and pricing shows the current order rates. Check locations for current mobile 4G/5G and residential availability. Both pools use the connection format described in the setup docs.

Price per GB by order size, EUR
Order sizePer GB
1 to 4 GBEUR 4.50
5 to 24 GBEUR 3.90−13 %
25 to 49 GBEUR 3.50−22 %
50 to 99 GBEUR 3.20−29 %
100 to 249 GBEUR 2.90−36 %
250 GB and moreEUR 2.50−44 %

GB never expire. What you buy stays on your balance until you use it; a new purchase adds to the same balance. The trial is 0.5 GB for EUR 2.90, once per customer.

The per-GB ladder these tests draw on. One balance covers both pools.

What is not allowed

What is not allowed: test against your own endpoints or a public echo service, and use the result for lawful work such as price monitoring, ad verification, market research and QA. Probing infrastructure you do not own, credential stuffing and account farming are refused. See the acceptable-use policy.

How to test a proxy with curl · Portproof