Server-Side Verification
Validate fingerprints on your backend to prevent spoofing.
Why Server-Side Verification?
Client-side fingerprinting can be spoofed. A malicious user could intercept the SDK response and replay a different visitorId to your application. Always verify on the server before making trust decisions like granting trial access, processing payments, or allowing votes.
This guide covers verifying visitorId values from the Identify product. If you want to classify API callers directly on the server without a client SDK at all, see the Sentinel guide — it uses @fingerprintiq/server to inspect every incoming request at the middleware level.
Never trust client-side data alone. The visitorId sent from the browser is user-controlled. Verify it against the FingerprintIQ API using your server-side secret key.
The Verification Pattern
Identify on the client
Call fiq.identify() in the browser and get a visitorId.
Send the visitorId to your server
Include the visitorId in your API request body or as a header — not as a query parameter (avoid logging it in access logs).
Verify with the FingerprintIQ API
Fetch the visit history for that visitorId from the FingerprintIQ API using your server-side secret key.
Apply your trust logic
Check confidence, bot probability, recency, and any risk factors before making a decision.
Client Code
typescript// 1. Identify the device const result = await fiq.identify(); const { visitorId, confidence, botProbability } = result; // 2. Send visitorId to your server with the user's request const response = await fetch('/api/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ visitorId }), }); const { verified } = await response.json();
Server Implementation
app.post('/api/verify', async (req, res) => {
const { visitorId } = req.body;
const fiqRes = await fetch(
`https://api.fingerprintiq.com/v1/demo/visits/${visitorId}`,
{
headers: {
'X-API-Key': process.env.FIQ_SECRET_KEY,
'Content-Type': 'application/json',
},
}
);
const data = await fiqRes.json();
const latestVisit = data.visits[0];
const isLegitimate =
data.totalVisits >= 1 &&
latestVisit?.confidence >= 0.8 &&
latestVisit?.botProbability < 0.3 &&
Date.now() - latestVisit?.timestamp < 5 * 60 * 1000; // within 5 minutes
res.json({ verified: isLegitimate, visits: data.totalVisits });
});import { Hono } from 'hono';
const app = new Hono();
app.post('/api/verify', async (c) => {
const { visitorId } = await c.req.json();
const fiqRes = await fetch(
`https://api.fingerprintiq.com/v1/demo/visits/${visitorId}`,
{
headers: { 'X-API-Key': c.env.FIQ_SECRET_KEY },
}
);
const data = await fiqRes.json();
const latestVisit = data.visits[0];
const isLegitimate =
data.totalVisits >= 1 &&
latestVisit?.confidence >= 0.8 &&
latestVisit?.botProbability < 0.3;
return c.json({ verified: isLegitimate });
});// app/api/verify/route.ts
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
const { visitorId } = await req.json();
const fiqRes = await fetch(
`https://api.fingerprintiq.com/v1/demo/visits/${visitorId}`,
{ headers: { 'X-API-Key': process.env.FIQ_SECRET_KEY! } }
);
const data = await fiqRes.json();
const latestVisit = data.visits[0];
return NextResponse.json({
verified:
latestVisit?.confidence >= 0.8 &&
latestVisit?.botProbability < 0.3,
totalVisits: data.totalVisits,
});
}Security Best Practices
The checks below are layered defenses. Implement all of them — skipping any one check leaves a gap an attacker can exploit.
-
Verify recency — Check that the latest visit timestamp is within the last 5 minutes. A
visitorIdfrom an hour ago should not grant access to a current session. -
Check confidence — Reject fingerprints with
confidencebelow 0.7. Low confidence means too many signals failed to collect, which is itself suspicious. -
Monitor bot probability — Flag visitors with
botProbabilityabove 0.3 for review. Block those above 0.7. -
Check risk factors — Inspect the
riskFactorsarray for signals likeHEADLESS_BROWSER,TOR_EXIT_NODE,UA_TLS_MISMATCH, orSOFTWARE_RENDERER. -
Use server-side API keys only — The secret key used to call the FingerprintIQ API must never be exposed to the browser.
Store verified visitorId values in your session after the first verification. This avoids a round-trip to the FingerprintIQ API on every authenticated request while still establishing the initial trust anchor.