From FindIP Engineering

How to detect risky signups without blocking legitimate VPN users

"Block all VPNs" is the fastest signup rule to write and one of the most expensive to keep. Here is a policy that catches the same abuse and leaves ordinary VPN users alone.

When fake accounts start arriving through VPNs and proxies, the obvious rule is to refuse signups from them. It works on the first day. Then support hears from a customer on a corporate VPN, a journalist on a privacy service, and a traveller on hotel Wi-Fi, and the abuser who caused the rule has already moved to an address your list does not know.

The rule fails because it treats one signal as a verdict. A VPN flag describes a connection. It says nothing about the person using it.

Who is actually behind a VPN flag

The same flag covers very different traffic:

  • Employees whose company routes all traffic through a corporate gateway.
  • People who run a consumer VPN all day for privacy, or a built-in relay such as iCloud Private Relay.
  • Travellers and people on networks they do not trust.
  • Someone creating their tenth trial account this week.

Only the last one is your problem, and the VPN flag is the one thing all four have in common. To tell them apart you need what differs.

What separates abuse from privacy

Abusive signups rarely show one signal. They show combinations, and the combinations are what a policy should be written against.

SignalOn its ownBecomes meaningful with
VPNCommon among legitimate users. Observe.Rotation in the session, or repeated signup attempts
Hosting or datacenter networkUnusual for a person at a signup form. Worth a closer look.Almost anything else on this list
ProxyLess common than VPN in ordinary traffic.Multiple accounts from the same network
TorLegitimate for some audiences, rare for most products.A high-value action such as a paid trial
Known malicious IPThe strongest single signal here.Stands on its own
IP or network rotation in one sessionMobile users change IP too. A change of network or country is rarer.Any anonymizing signal

A person on a corporate VPN shows one row of that table and nothing else. A scripted signup through a rotating proxy pool on rented servers shows three or four.

Step 1: observe before you decide

You cannot write a fair policy for traffic you have not looked at. Install FindIP Shield on the signup page and leave it in monitor mode, which is how it starts. Nothing is challenged or stopped.

npm install @findip/shield
import { init, track, getSession } from '@findip/shield';

init({
  siteKey: 'pub_xxxxxxxxx',
  privacyMode: 'balanced',
  autoTrack: true,
  autoDetectForms: true,
});

await track('signup_attempt', { plan: 'free' });

After a week you will know what share of your signups arrive through a VPN, and how many of those went on to become real accounts. That number is the cost of a blanket block, measured on your own traffic.

Shield records the event, the score and the reasons. It does not receive the email, the password or any other form value.

Step 2: write the policy against combinations

Shield returns a recommendation for each event: allow, monitor, challenge or block. It is deliberately cautious on sensitive actions: a VPN on a signup attempt is recommended for challenge, and Tor or a known malicious IP for block. A recommendation on its own stops nothing. It is an input, and the policy is yours.

That distinction is what makes room for VPN users. A reasonable starting policy:

What Shield reportsSignup response
allowContinue normally.
monitor (for example a hosting or proxy network with nothing else)Continue normally and keep the event for review.
challenge, and the only signal is a VPNContinue normally. This is the exemption that keeps ordinary VPN users out of your friction.
challenge, with hosting, proxy or rotation as wellAsk for something proportionate: a Turnstile check, or email confirmation before the trial starts.
blockStop only under a server-side policy you have written down, with a way for a real person to recover. Many teams start by challenging here too.
unknown statusIntelligence was unavailable. Continue with your normal checks; unknown is neither safe nor risky.

The third row is the whole point of this guide. Step 4 shows it as code.

Step 3: challenge instead of blocking

A challenge is the honest response to uncertainty. A real person on an unusual network passes it in a moment. A script loses its economics. And when you are wrong, the cost is a few seconds instead of a lost customer.

In Shield you set the in-page response per form in the dashboard. Pick the signup form Shield discovered and choose an enforcement type, which says what the page does for each recommendation. There are two sensible choices here:

  • Verify (Turnstile). A challenge recommendation shows a Cloudflare Turnstile check, and the pass lasts for the rest of the session. A VPN user is asked once and continues. This is the simplest setup and already far gentler than a block.
  • A custom type that lets challenge through. Nothing happens in the page for a challenge recommendation, and your server applies the VPN-only exemption from step 4. Use this when you want no friction at all for a VPN on its own.

The enforcement guide covers every option. If a decision does not arrive in time, the form submits as normal.

In-page responses are friction, not a boundary

They stop automation that runs your page in a real browser, which is most of it. They do not stop someone who posts to your endpoint directly. The step below is what closes that gap.

Step 4: make the final call on your server

Send the Shield session ID with the signup request and verify it from the backend before creating the account.

// Browser
const { sessionId } = getSession();

await fetch('/api/signup', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email, password, findip_session_id: sessionId }),
});
// Server
const response = await fetch(
  'https://shield.findip.net/v1/shield/sessions/verify',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.FINDIP_SHIELD_SECRET_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      session_id: req.body.findip_session_id,
      event: 'signup_attempt',
    }),
  },
);

const result = await response.json();

if (!result.verified || result.risk_status === 'unknown') {
  return createAccount();            // normal checks still apply
}

const { network, ip_rotating, asn_rotating } = result.session;

// A VPN alone is not a reason to add friction.
const vpnOnly =
  network.vpn &&
  !network.hosting && !network.datacenter && !network.proxy &&
  !network.tor && !network.malicious &&
  !ip_rotating && !asn_rotating;

if (result.recommendation === 'challenge' && !result.challenge_passed && !vpnOnly) {
  return requireEmailConfirmationBeforeTrial();
}

return createAccount();

The verify response includes the session's network flags and rotation fields, so a rule such as "never add friction for a VPN on its own" can be written explicitly instead of hoped for. challenge_passed is true once the visitor completed a Turnstile challenge that Shield verified for the session.

Step 5: connect the session to the account

Once the account exists, pass its ID to the SDK. Shield hashes it in the browser, so a risky session can later be traced to an account in your system without Shield holding the raw email.

import { identify } from '@findip/shield';

identify({ userId: newUser.id });

This is what lets you answer the question a block rule never could: of the signups that came through a VPN last month, how many became paying customers?

What to avoid

  • A rule that names one signal. Write rules against the recommendation or against combinations.
  • Blocking by country as a stand-in for blocking abuse.
  • Challenging on every signup because a few were bad.
  • Enforcing on a browser-side result without the server check.
  • A block with no recovery path. Someone real will hit it.

Checklist

  • One week of monitor-only data on the signup form.
  • The share of legitimate signups using a VPN, measured.
  • A written response for each recommendation and for unknown.
  • A challenge, not a block, as the default response to risk.
  • Server verification before the account is created.
  • Hashed user ID attached so outcomes can be reviewed later.

Measure your own VPN signups first

Add Shield to the signup form and watch for a week. You will see how many signups arrive through VPNs, proxies and hosting networks before you decide anything.

Start with Shield free Open the quickstart VPN and proxy signup detection