You open a "what's my IP" page, it shows an address in the country you expect, and you conclude your network is fine. Then ChatGPT loops on a verification page, or Claude says it's unavailable in your region, and nothing adds up.
Often the explanation is boring: the "what's my IP" page and the AI service never saw the same connection. An IP check only reports the public exit used to reach that site, over that address family, through whatever rule matched that hostname. An AI service on a different hostname can take a different path.
This post covers the three usual causes (dual-stack, domain-based proxy rules, terminal vs. browser), how to ask the service side which IP it sees, and the curl commands I use. At the end I mention a small tool I built for this, IP for AI, but you can do everything here with curl alone.
Cause 1: IPv4 and IPv6 are two different exits
On a dual-stack network your machine has two public identities. They can sit on different ASNs, show up as different regions, or one can go through a proxy while the other goes direct. A common setup: the proxy covers IPv4, and IPv6 quietly bypasses it.
Which family a given request uses isn't fixed. DNS can return both A and AAAA records, the OS sorts the candidates (RFC 6724 default address selection), and the client races connection attempts (RFC 8305, Happy Eyeballs v2). The page, the sign-in redirect and static assets can sit on different hostnames, so one session can even mix families. Seeing IPv4 on one site tells you nothing about what a different hostname got a second later.
One TCP connection has exactly one source address, so any single check sees one family. Force each family on purpose:
curl -4 -sS https://ipforai.cc/json
curl -6 -sS https://ipforai.cc/json
If both come back with the same country and network type, good. If IPv4 says one region and IPv6 says another, you've found a variable worth testing.
Cause 2: domain-based proxy rules
Rule-based clients (Clash/Mihomo and similar) choose a route per hostname. That's the point of split routing, and it also means "my IP" depends on which domain you ask.
Things that typically go wrong:
- The rule set covers
chatgpt.combut not some other hostname the app actually requests. -
DOMAIN-SUFFIXandDOMAIN-KEYWORDbehave differently. Suffix matches a domain and its subdomains. Keyword checks for a substring in the hostname, not the URL path. - Rule order: an earlier broad rule catches the request before your AI rule does.
- DNS caching, or a client that doesn't cover every app, makes results look inconsistent between runs.
The opposite case also happens: every AI domain resolves to the same exit, and people assume split routing is "off" when it's working exactly as configured. A shared exit isn't automatically a bug. Compare against your rules, not against a guess.
Cause 3: your terminal is not your browser
curl in a terminal follows the HTTPS_PROXY / https_proxy environment variables and your OS routing. Your browser may use a system proxy, an extension, or something else. A VPS has no browser at all. If you test in one place and the failure happens in the other, you've compared two different paths.
Ask the other side: the cdn-cgi/trace endpoint
A local check can't tell you what a remote service saw. The useful trick is that sites served through Cloudflare expose a plain-text diagnostic endpoint at /cdn-cgi/trace. Several AI front-ends are on Cloudflare, so you can ask their edge which IP your request arrived from:
curl -s https://chatgpt.com/cdn-cgi/trace
The output is key=value lines. Trimmed, it looks like this:
h=chatgpt.com
ip=<your public exit as seen by this hostname>
loc=JP
http=http/2
tls=TLSv1.3
warp=off
ip is the source address of that connection and loc is the country Cloudflare assigns to it. Because the request went to chatgpt.com, it went through whatever proxy rule matches chatgpt.com. That's the point.
Run it per hostname and per family:
for host in chatgpt.com claude.ai grok.com; do
for fam in -4 -6; do
printf '%-12s %s ' "$host" "$fam"
curl $fam -s -m 8 "https://$host/cdn-cgi/trace" \
| grep -E '^(ip|loc)=' | tr '\n' ' '
echo
done
done
Then compare with the exit a neutral site sees for the same family:
curl -4 -sS https://ipforai.cc/json | jq -r '.ipv4.ip // .ipv6.ip'
curl -6 -sS https://ipforai.cc/json | jq -r '.ipv6.ip // .ipv4.ip'
(/json fills only the family the server saw the request arrive on, so the other field is null. -4 controls how your machine connects. If there's another relay layer in front of you, the server can still record the other family, so print whichever field is filled.)
If chatgpt.com over IPv4 shows a different IP or country than the neutral check over IPv4, that hostname is taking a different path. Sometimes that's intended, sometimes it's a rule that doesn't do what you think.
Limits of this trick
- It only works on hostnames served through Cloudflare. If a service isn't, there's no
/cdn-cgi/traceand you get no echo. Don't read an exit into a missing answer. - It shows the exit for that hostname. An app's API, auth and asset hosts may route differently. The trace on a front-end domain is evidence, not proof of what every request in a session used.
- A matching exit doesn't mean your account, subscription or region is fine. It removes one network variable. That's all.
A debugging loop that actually attributes the cause
- Record both exits separately: IP, country, ASN, and whether it looks like data-center or residential.
- Record the service-side exits from the trace endpoints for the services you care about.
- Check whether failures follow one address family or one hostname.
- Change one thing, retest, and record again. Proxy covering IPv6, a single rule, one DNS setting. Change three things at once and you won't know which fix worked.
- A short IPv4-only or IPv6-only test is a fine diagnostic. Don't leave it as your permanent setup; fix the proxy coverage or routing instead.
- If the exits are aligned and stable and the service still fails, stop blaming the network. Look at the browser session, the exact error message, and the provider's status page.
The tool I built for this
After running those loops by hand too many times, I put them into ipforai.cc. It's free and needs no account. What it does today:
- Profiles your current IPv4 and IPv6 exits separately: region, ASN, network type (data-center / residential evidence), routing hints, and proxy / VPN / Tor and risk-list signals where data exists.
- Computes a 0–100 network environment score with itemized evidence. It's a reference for the network only, not any platform's official score, and not a ban predictor.
- An AI routing exit page that compares the exits supported AI endpoints echo back against the site's own exit.
- DNS-exit and WebRTC comparisons in the browser, plus symptom-first guides such as IPv4/IPv6 exit mismatch.
For servers there's a read-only POSIX sh script: it needs only curl, writes no config, installs nothing and needs no API key. It probes public endpoints of 11 AI services and compares routing exits for 20+ services against the same-family local exit. Please read it before running it:
curl -fsSL https://ipforai.cc/sh -o /tmp/ipforai-check.sh
less /tmp/ipforai-check.sh
sh /tmp/ipforai-check.sh
The cloudflare-mode routing probes in that script use the same cdn-cgi/trace approach shown above, so nothing in it is magic. Usage and output are documented at /cli, and the script mirror and JSON docs are on GitHub.
Takeaway
"My IP" isn't one value. It depends on the address family, the hostname, the matching rule and the client making the request. Before you blame your IP's "reputation," check that the service saw the IP you think it did. Ask the service side, check both families, and change one variable at a time.
If you've hit a dual-stack or routing edge case these checks don't explain, I'd like to hear about it in the comments.
Top comments (0)