iOS apps

Detect VPN and proxy users in your iOS app

An on-device check only sees a VPN configured on this iPhone. FindIP Shield scores the network behind each signup, login and checkout from outside the device, and your backend makes the call. One Swift package, no dependencies, no tracking.

Free Preview · 10,000 events per site per day · No credit card · Terms apply

VPN Proxy Tor Privacy relay Hosting network Malicious IP
The problem

The phone can tell you about its own VPN. Not much else.

The usual Swift answer reads the system proxy settings and looks for VPN interfaces. It is an undocumented heuristic, and it only sees a VPN running on this phone. A proxy further along the path, a residential proxy, a Tor exit or a script replaying your API from a hosting network never touches it.

And whatever the app concludes, a modified app can say the opposite. The answer has to come from outside the device, and the decision from your server.

One event, explained

What one of these looks like in Shield

Every visit and form submit is recorded with a score, the reasons behind it, the session around it, and what the page did. A hosting network or a VPN is used legitimately every day; the reasons are there so you can judge, not a verdict on a person.

A recorded eventSample data

login_attempt

62 · high

/screen/login · a moment ago

Why this risk score
  • vpn_detectedThe IP belongs to a commercial VPN. Common among real users; on its own, no reason to refuse anyone.
  • hosting_networkThe IP belongs to hosting infrastructure rather than a mobile or home network.
  • login_attemptA sensitive action: the reasons above weigh more here than on a screen view.
Session
App
iOS app · version 4.2.1
Network
AS64500 · hosting · Germany
Networks in session
2
Screen
login
What happened
Shield recommended
challenge
A verdict for your site to use. On its own it stops nothing.
Observed in the page
Step-up asked
This app's backend verified the session, saw challenge, and asked for an emailed code before signing in. A real customer on a VPN types the code and carries on.
How Shield helps

From the first login event to a decision on your server

The verdict is advice to the app. Your backend makes the call.

Start by monitoring

Create an app site and ship the SDK in a debug build. Nothing is refused: every tracked action shows up with a 0 to 100 score and the reasons behind it.

Read the reasons

vpn_detected, proxy_detected, hosting_network and the rest, each with a plain sentence, plus the session: networks, countries, platform and app version.

Decide per action

A login on a VPN can usually continue; a signup that earns a bonus might ask for a code. Your backend chooses from the recommendation.

Verify on your server

Before the account is created or the reward is paid, post the session ID with your secret key. An app can be modified; the server check is the one that counts.

Tune with rules

Custom rules by platform and app version change the verdict for one app, for example retiring builds below 4.0, without touching your website.

What Shield shows you

The evidence, then the response you choose

Each anonymizer as its own flag

VPN, proxy, Tor, privacy relay, hosting network and malicious IP as separate reasons, with the network and country, on every action the app sends.

Checked by your backend

The app sends the session ID with its request. Your server verifies it with the secret key and gets the recommendation plus the session: platform, networks, countries, IP rotation.

Rules by platform and version

Challenge a country only in the iOS app, or block builds below 4.0. A matching rule changes the verdict the app and your backend receive.

Setup

Add it with Swift Package Manager

iOS 15 or later (also tvOS, visionOS and macOS). No dependencies; the package ships a privacy manifest.

Create a mobile app site

Sign in, choose Mobile app, and register your bundle identifier. You get a public site key for the app and a secret key for your server.

Add the Swift package

In Xcode, File, Add Package Dependencies, paste github.com/findip-net/findip-shield-swift, and call FindIPShield.configure(siteKey:) once at launch.

Track the sensitive action

Call track before the login or signup request and send verdict.sessionId with it. Your backend verifies the session before it acts.

Prefer another route? Building for Android too? See Shield for Android apps.

In your code

Configure once at launch, then track the action and send the session ID with the request. track never throws for network problems.

import FindIPShield

// At launch
try? FindIPShield.configure(siteKey: "pub_your_app_key")

// On the login button
let verdict = try await FindIPShield.track(ShieldEvents.loginAttempt, screen: "login")
var request = URLRequest(url: loginURL)
request.setValue(verdict.sessionId, forHTTPHeaderField: "X-Shield-Session")
// verdict.risk.recommendation: .allow, .monitor, .challenge or .block
// No answer within 5 s: .unknown with .monitor, never .allow
Runnable SwiftUI demo

Read the session ID the app sent, verify it with the site's secret key, then allow, ask for a step-up, or refuse. The secret key never ships in the app.

curl -X POST https://shield.findip.net/v1/shield/sessions/verify \
  -H "Authorization: Bearer $FINDIP_SHIELD_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"session_id": "<the X-Shield-Session header>", "event": "login_attempt"}'

# → {"verified": true, "risk_status": "available", "risk_score": 62,
#    "recommendation": "challenge", "session": {"platform": "ios", ...}}
Server verification reference
Boundaries

What a risk score is, and what it is not

An assessment, not proof. A VPN, a hosting network or a changing IP is common for privacy-conscious and corporate users too. Shield explains the reasons so you can decide; it does not pass judgement on a person.
Friction, not a security boundary. Anything decided in the browser can be bypassed by someone who controls it. For decisions that matter, verify the session from your server with your secret key.
Unknown is never safe. Where no intelligence was available the status is unknown. Shield fails open: if a decision does not arrive, the form submits as normal.
Questions

Frequently asked

You can look for a VPN configured on this iPhone, through an undocumented interface heuristic. It cannot see proxies elsewhere on the path, hosting networks or Tor exits, and a modified app can report anything it likes. Shield classifies the network from the server side, and your backend verifies the session.

No. No advertising ID, no identifierForVendor, no device fingerprint. A random visitor ID lives in the Keychain on this device only, so reinstalling the app for a fresh trial stays visible. Strict privacy mode sends no visitor ID at all.

Device ID (the visitor ID) and Product Interaction (event names), not linked to the user, not used for tracking, purpose App Functionality. The package ships a privacy manifest. If you call identify, also declare the identifiers you pass; they leave the device only as hashes and ciphertext.

track answers within five seconds with an unknown verdict and the recommendation monitor, never allow. The app carries on; your backend decides how much an unknown should count for that action.

Not in this release. Shield judges the network and the session, not whether the binary is genuine. If you need that proof too, pair it with App Attest on your own backend.

10,000 events per app site per day, no credit card. Send the sensitive actions, not every screen. Beyond the quota events are refused with a 429 and returned as unknown, never as safe.

Start by watching one real flow.

Create a free Shield site, install it, and look at the first real event before you decide on any response.

Add my app

Next: create a free mobile app site, register your app's ID, and see the first real event from a debug build. Nothing is refused until you decide.

Free Preview · Monitor first · Stop, slow, challenge, or redirect risky submits when you choose