The "Connected" badge in your client only means the local tunnel handshake succeeded. It says nothing about whether data actually leaves through the route. To judge whether a VPN is working, look at three signals that can each fail independently: the exit IP, where DNS resolution happens, and where a specific app's traffic really goes. If two of the three don't line up, treat it as not working.

Start by separating three things: connection status, exit IP and DNS resolution

Connection status is the client's own judgement, and it only covers the tunnel being established. Once the tunnel is up, which traffic enters it, who resolves domain names, and whether a given app ignores system settings are all decided by other configuration. So test each one separately — don't let a single "can I open a web page" result stand in for everything.

The three signals, and what normal versus abnormal looks like
Check How to test Normal Warning sign
Exit IP Look up your public address before and after connecting The address changes and matches the exit region of the route you picked Identical to the pre-connection address, or in a region unrelated to the one you picked
DNS resolution Check the resolver's public exit and compare the answers The resolver exit is in the same region as the route exit Resolution still happens on the local network, or answers point to local nodes
Per-app routing Run a separate exit check inside the target app That app's exit matches the route Only this one app still uses the local exit while the others are fine

Suggested order: exit IP first, because it's the most obvious; DNS next, because it's the most overlooked; per-app checks last, because they're the easiest to misread.

Caches lie. The second load of the same page may come from a local cache or a CDN edge node, and it will still render after the route has dropped. Use a private window, or a domain you haven't visited before.

Method 1: check the exit IP to see where traffic leaves from

The exit IP is the source address external sites see. The method is simple: look it up with the connection off, connect, then look it up again. If the two results differ and the new address sits in the region you selected, this step passes. A command-line lookup returns plain text and doesn't depend on any client UI.

# macOS / Linux terminal
curl -s https://ifconfig.me/ip

# Windows PowerShell
(Invoke-RestMethod https://ifconfig.me/ip)

# Check the IPv6 exit separately
curl -s -6 https://ifconfig.me/ip

You can also open an IP-check page in a browser, but make sure it shows the raw exit address rather than a city guessed from CDN data — those guesses are often far from the real exit location and make a poor basis for a conclusion.

If the exit IP doesn't match the region you picked, don't call it a failure yet

These three cases are normal and need to be read separately:

  • Relay routes: the entry is nearby and the exit is in the target region, so what you should see is the exit address. If you see the entry address, that traffic was matched as direct by your routing rules.
  • Stale IP geolocation: for announced blocks or recently moved addresses, the location label can lag behind reality. Cross-checking against a second lookup source is more reliable.
  • Dual stack only half covered: IPv4 goes through the route while IPv6 still uses the local exit. Browsers and operating systems usually prefer IPv6, so the page looks "not connected" when it's really taking the other path.

To check dual stack, use the command with -6 above, then run it again without -6. Read both results together; either one alone will lead you to the wrong conclusion.

Method 2: check DNS resolution to see where domains are resolved

A DNS leak means traffic goes through the tunnel while domain names are still handed to a resolver on the local network. Resolution then answers based on your local position, and platforms receive two conflicting location signals. Sometimes it shows up as slow loading on an otherwise working connection, sometimes as an extra verification step at login, and streaming services simply report the wrong region.

# See what a domain currently resolves to
nslookup www.wikipedia.org

# Look up the resolver's public exit address (no @ argument, so the system resolver is used)
dig +short TXT o-o.myaddr.l.google.com

# The same lookup on Windows
nslookup -type=TXT o-o.myaddr.l.google.com

The second command returns the recursive resolver's public exit address. Compare it with the exit IP from step one: if both sit in the same region, resolution is following the route; if the resolver is still your local ISP, DNS hasn't been taken over.

Three reasons DNS quietly leaks

  1. Secure DNS is enabled in the browser. It bypasses the system resolver, so a client that takes over DNS can't intercept it. Turn off secure DNS in the browser settings, test again, and compare the two results to pin it down.
  2. The client hands DNS to the local system. Some clients default to "resolve locally, connect remotely", so names are already resolved before the tunnel. Switch to resolving on the route side and retest.
  3. Your routing rules already send this domain direct. Under rule mode that's expected behaviour — test the domains that are actually routed, and don't use a direct domain as your sample.

With nslookup, watch the "Server" line it prints — that's the resolver currently in effect. If that line still shows your router or ISP address, the answers below it are worthless.

Method 3: per-app checks to confirm a specific app uses the route

Per-app proxying is most common in Android clients; on desktop most setups are global TUN, and per-app routing needs routing tables, a dedicated browser proxy or a virtual machine. This is the easiest item to misread, because clients use opposite wording: some mean "proxy only the selected apps", others mean "skip the selected apps".

  1. First confirm which way the selection works

    Open the per-app list and read whether it says "proxy only selected apps" or "skip selected apps". Get this backwards and everything after it is wrong — no amount of careful comparison will help.

  2. Check the exit IP from inside a selected app

    Open the IP-check page in the app's built-in browser, note the result, and compare it with the baseline you recorded before connecting. If the address changed and sits in the region you picked, this item passes.

  3. Check again from an unselected app or the system browser

    The two results should differ. If they're the same, per-app routing isn't working and all traffic is still taking one path.

  4. If the results match, check two things

    First, whether the list direction is reversed; second, whether the app uses its own network stack. Built-in proxies, proprietary protocols and system-level downloaders can all bypass system settings, and no client setting will fix those.

  5. Keep a baseline record

    A major OS update or a client upgrade can reset the per-app list. Re-running the baseline takes minutes and saves far more time than hunting for the cause after something breaks.

When the desktop client has no per-app switch, judge it differently: on the same machine, run the check in the system browser and in an app with its own proxy settings, and see whether the exits match. Matching exits mean global capture is working; different exits mean an app is bypassing the tunnel and needs its own proxy configuration.

Looks connected but isn't: common cases side by side

These come up most often in troubleshooting, so you can use them as a checklist:

  • ❌ The client says connected, but the subscription link expired long ago, the node list is stale, and you're connected to an address that's already offline.
  • ❌ In system proxy mode, only apps that read the system proxy settings use the route; apps that ignore them keep connecting directly.
  • ❌ The browser has secure DNS on, so resolution bypasses the client's takeover: the exit IP changes while resolution still happens locally.
  • ❌ Only IPv4 is captured and IPv6 still uses the local exit; dual-stack sites prefer IPv6, so pages behave as if you aren't connected.
  • ❌ The per-app list is set the wrong way round — you think the app is proxied when it's actually excluded.
  • ❌ Routing rules send the target domain direct, or the rule set hasn't been updated in a long time.
  • ✅ Working state: the exit IP differs between disconnected and connected and sits in the region you picked, the resolver exit is in the same region as the route, and the target app shows the route exit when checked on its own.

Use a hard pass/fail rule: if two of the three signals don't line up, treat it as not working. Check subscription status and routing mode in the client first, then DNS and dual stack, and only then suspect the route itself. Reverse that order and you'll keep reworking parts that were never broken.

Run the whole verification once through

Turn the methods above into a fixed sequence and run it whenever you change networks or devices, or after a major client update — it takes a few minutes.

  1. Disconnect and record a baseline: IPv4 exit, IPv6 exit, resolver exit — write all three down.
  2. Connect to a route and repeat the same three lookups.
  3. Compare: did the exit change, is the region the one you selected, and did the resolver follow?
  4. Open the target app and run a separate exit check inside it.
  5. If any item hasn't changed, work through the checklist in the previous section, change one setting at a time and retest immediately.
  6. Switch to another route and run it again, to rule out a single bad route instead of writing off the whole service.
90+ Countries and regions
210+ Routes in total
Unlimited Devices online at once
7 days Money-back, no questions asked

More routes means cheaper retesting: if one route's exit is wrong, switch to another route in the same region and run the same three lookups to tell a route problem from a local configuration problem. The route list is grouped by region, which makes picking a backup in the same region easy. This service keeps no logs, and you can check subscription usage and data-pack balance yourself in the dashboard; if something turns up within the 7-day money-back window, start the refund instead of working through a long checklist first.

Keep a record of each verification: date, network, route region, three results. If something does go wrong, that record is worth more than any verbal description, and it shows which change the problem started with.

Bottom line: whether a web page loads is not the test — three matching signals are. Exit IP, DNS resolution location and per-app routing all line up, and only then is it actually working.