Skip to content
VPNDetection

Geofencing a US state by IP: every state has VPN exits

Every US state holds VPN and residential proxy exits, so an IP-only geofence lets them through. Build one in Express that geolocates, then checks the address.

Published 6 min readLast updated on

On this page
  1. What Nevada said about Kalshi's geofence
  2. Every state holds VPN and proxy exits
  3. Two checks, in code
  4. Unknown is not outside
  5. Where IP stops

In May, a Nevada court ordered Kalshi to stop offering sports, election and entertainment contracts to anyone located in Nevada. Kalshi built a geofence on the visitor's IP address, and the state went back to court a month later saying it didn't work. IP geolocation puts most visitors in the right state; it fails when the address isn't the visitor's own, and every US state and DC holds thousands of VPN exits and residential proxy exits. This post counts them, then builds a Nevada geofence in Express that geolocates the address and refuses to trust the answer when the address belongs to a VPN, a proxy or a server. It's about the engineering, not legal advice.

What Nevada said about Kalshi's geofence

The court's order of May 18 required Kalshi to geofence its contracts so they weren't offered to anyone in Nevada. On June 12 the Nevada Gaming Control Board asked the court to hold Kalshi in contempt (the Board's release). According to InGame's report on the filing, the state said Kalshi's in-house tool relied on the visitor's IP address, which a VPN gets around. It also said internet providers aren't required to assign addresses by state, and that mobile addresses aren't fixed to one place, so blocking one state by IP can be unreliable with no VPN at all. It cited one estimate putting the success rate of state-level IP blocking at 55% to 80%, and its investigators bought contracts from inside Nevada without a VPN. Kalshi had spent $190,000 on the tool and used "family and friends" to test it.

In July, Kalshi agreed to switch to a third-party geolocation provider by August 12 or pay $120,000 a day (InGame). On August 15 the Board told the court that investigators had still bought banned contracts from Nevada on their phones. Kalshi answered that they had used a previous version of its app, one not subject to geofencing, and had misrepresented their residence (The Nevada Independent). A Washington court has also ordered Kalshi to geofence that state, by September 2 (OPB), and Michigan's order requires a third-party geolocation provider licensed by its gaming board (InGame).

The same problem comes with sweepstakes, fantasy sports, state-licensed content and promotions limited to one state.

Every state holds VPN and proxy exits

Geolocation reports where an address is. For a VPN server or a residential proxy exit, that's where the server or the exit is, and the visitor can be anywhere. From our data on October 19, 2026, each address placed in a state by IP geolocation:

VPN addressesVPN servicesResidential proxy addresses, last 30 days
United States2,747,1893592,304,935
California427,228234236,215
New York321,161189194,967
New Jersey110,75514251,608
Washington72,91011855,742
Michigan41,0703743,942
Nevada28,8325716,409
Vermont, the fewest3,92972,110

All counts are IPv4. The VPN columns are consumer VPN services; corporate VPN gateways and private relays are left out. 14 states and DC have exits from 50 or more VPN services, and 33 states and DC from 10 or more.

Both directions matter. A Nevada resident who wants past a Nevada geofence picks a server in California. An operator licensed to serve only New Jersey has the reverse problem: anyone outside the state can pick one of 110,755 VPN addresses in New Jersey. A residential proxy exit is someone's home connection, so to geolocation it looks like any other customer of that ISP, wherever that home is.

Two checks, in code

Geolocate the address to a state, then ask whether the address is the visitor's own. If it's a VPN, a proxy, Tor or a server, the state you got is the exit's, and you don't know where the visitor is. Here's that as an Express route with vpndetection-express, geofencing Nevada:

js
import express from 'express';
import { vpndetection } from 'vpndetection-express';
import { regionOf } from './geo.js'; // your IP geolocation lookup

const BLOCKED = 'US-NV';

// Each of these means the address isn't the visitor's own connection.
const NOT_THEIRS = [
    'isVpn', 'isResproxy', 'isMobproxy', 'isDcproxy', 'isPubproxy',
    'isTor', 'isRelay', 'isHosting',
];

const app = express();
app.set('trust proxy', 'loopback'); // your reverse proxy, on this machine
const check = vpndetection({ apiKey: process.env.VPNDETECTION_API_KEY });

app.post('/trade', check, (req, res) => {
    const { result, error } = req.vpndetection;
    const masked = NOT_THEIRS.filter((flag) => result?.[flag]);
    const region = regionOf(req.ip);
    const decision = decide(region, masked, error);
    const kind = error?.kind ?? null;
    console.log(JSON.stringify({ region, masked, error: kind, decision }));

    if (decision === 'allow') return res.json({ ok: true });
    if (decision === 'deny') {
        return res.status(403).json({ error: 'Not available in Nevada.' });
    }
    res.status(403).json({ error: 'Share your location to continue.' });
});

function decide(region, masked, error) {
    if (region === BLOCKED) return 'deny'; // VPN or not
    if (error) return 'verify'; // no answer is not a clean answer
    if (masked.length > 0) return 'verify'; // the location is the exit's
    if (region === 'US') return 'verify'; // country only: unknown, not out
    return 'allow';
}

app.listen(3000, '127.0.0.1');

regionOf stands for whatever IP geolocation you use. It returns an ISO 3166-2 code such as US-NV, or US alone when it knows the country but not the state. For these runs it answered from a table of the four addresses we tested, each placed by IP geolocation on October 21.

isRelay is there because a relay's address isn't always in the visitor's state: an iCloud Private Relay user on "Use country and time zone" can come out anywhere in their country and time zone, as the Private Relay post covers. isHosting is there because a server's address is the datacenter's.

The middleware sits on /trade alone, so a lookup happens only where the decision matters. trust proxy names the reverse proxy in front of the app; without it, req.ip is the proxy's address, and you'd geolocate and look up your own server. The Express post tests what goes wrong with true or a hop count instead.

We ran it on October 21, 2026, with Express 5.3.0, vpndetection-express 2.0.18 and Node 22.23.3, on a key on the Max plan. Each visitor reached POST /trade from the same machine with its address in X-Forwarded-For, as a reverse proxy sends it. The Mullvad server is 23.234.72.2, in Los Angeles on Mullvad's own server list; the other addresses are real ones we don't print.

VisitorregionOfFlags setResponse
Mullvad serverUS-CAisVpn, isHosting403, share your location
Home broadband in NevadaUS-NVnone403, not available in Nevada
Home broadband in CaliforniaUS-CAnone200
Residential proxy exit in CaliforniaUS-CAisResproxy403, share your location

The log read:

{"region":"US-CA","masked":["isVpn","isHosting"],"error":null,"decision":"verify"}
{"region":"US-NV","masked":[],"error":null,"decision":"deny"}
{"region":"US-CA","masked":[],"error":null,"decision":"allow"}
{"region":"US-CA","masked":["isResproxy"],"error":null,"decision":"verify"}

With geolocation alone, the Mullvad server and the proxy exit are two more California addresses, and both would have traded.

The Mullvad answer, in part:

json
{
  "is_vpn": true,
  "is_hosting": true,
  "is_resproxy": false,
  "is_mobproxy": false,
  "is_pubproxy": false,
  "vpn": {
    "provider": "mullvad",
    "last_seen": "2026-10-21",
    "confidence": "high"
  }
}

It also carries location and asn, and location stops at the country, the United States here: the state still comes from regionOf.

Unknown is not outside

decide turns three kinds of not knowing into verify, never allow.

A failed lookup. The middleware fails open: when our API is unreachable, your quota is spent or the key is wrong, the request goes on with error set. With an invalid key, the Nevada home line was still refused, since its state alone settles it, and the other three were asked for their location:

{"region":"US-CA","masked":[],"error":"unauthorized","decision":"verify"}
{"region":"US-NV","masked":[],"error":"unauthorized","decision":"deny"}
{"region":"US-CA","masked":[],"error":"unauthorized","decision":"verify"}
{"region":"US-CA","masked":[],"error":"unauthorized","decision":"verify"}

A country with no state. Some geolocation answers stop at the country. An address that might be anywhere in the US might be in Nevada, so it verifies. The IP geolocation we used placed every US IPv4 range in a state on October 19; check what yours does.

A flag your plan doesn't include. isVpn, isTor and isRelay come back with a key or without one, and every paid plan adds isHosting and the proxy flags (pricing). A flag outside your plan is undefined, which this code reads as not set. Without a key, the Mullvad server was still asked for its location on isVpn, and the California proxy exit traded:

{"region":"US-CA","masked":["isVpn"],"error":null,"decision":"verify"}
{"region":"US-NV","masked":[],"error":null,"decision":"deny"}
{"region":"US-CA","masked":[],"error":null,"decision":"allow"}
{"region":"US-CA","masked":[],"error":null,"decision":"allow"}

A geofence that has to catch residential proxies needs the plan that answers isResproxy. The middleware keeps each answer for an hour, so an address that reaches /trade costs one lookup an hour, however many requests it sends. Past a few million visitors a day, look addresses up locally in the VPN IP database and the Residential Proxy IP database instead of calling the API.

Where IP stops

None of this knows where the visitor is. It knows when the address can't say, and Nevada's own investigators showed the other half: ordinary connections inside the state got through Kalshi's IP-only tool. And as the state told the court, a phone's address isn't fixed to one place.

What "share your location" leads to is your call. For a sweepstakes or a promotion, the browser's location prompt may be enough. For anything licensed, it's a geolocation provider your regulator accepts: Kalshi's July agreement with Nevada called for "a robust, multi-source geofencing solution provided by a third-party commercial provider", and Michigan's order requires one licensed by its gaming board. The IP check is the screen in front of it: it decides who can go straight through, and who has to prove where they are.

The same check for Utah's age verification law is in Utah SB 73: what a site can and can't know about VPN users.

Written by

  • Uman Shahzad

    Founder & CEO

    Uman has worked on IP data for over eight years, including as lead engineer at IPinfo. He built systems and practices that IP data companies now use to run at scale and keep their data accurate.

Get new posts by email

Or follow along in a feed reader.

RSS feed

Try it on your own traffic.

The detection these posts describe is one API call away, with SDKs for every major language.

00 · Unlimited

Unlimited lookups, no sign-up.

Paste it into a terminal. Any address answers its VPN, Tor and relay flags, where it is and which network announces it, with no daily or monthly cap.

curl "https://api.vpndetection.io/146.70.116.130"
{
"ip": "146.70.116.130",
"is_vpn": true,
"is_relay": false,
"is_tor": false,
"location": {
"country": "AT", ...
},
"asn": { "asn": 9009, ... }
}
01 · Sign up

Try every field, free.

Your first 100 lookups each day answer every field, the evidence behind each flag included.
The rest answer as Unlimited, with no cap.
No card and no sales call: the key is issued on signup.
A paid plan answers every field on every lookup.