Connect Cloudflare to deploy tracenow at the edge, with device fingerprinting injected automatically, no snippet to embed, no backend call required to start.
tracenow installs a managed Cloudflare Worker on your own zone. The Worker injects the
device fingerprinting script for you and calls /trace on every login, signup, or OTP. It
blocks bad traffic before it reaches your origin. You don’t add a snippet and you don’t write a
backend call to get started. Connect Cloudflare, pick your routes, click Deploy.Once it’s running you can add the optional server-side call to unlock
full per-user account-takeover protection (new device for this account, impossible travel,
brute-force on a known user).
A Cloudflare account with your domain added as a zone.
A Cloudflare API token with exactly two scopes:
Workers Scripts: Edit
Zone → DNS: Read
Create a dedicated token for tracenow at
dash.cloudflare.com/profile/api-tokens.
Select Create Custom Token, add the two scopes above, and scope it to the zone(s) you
want to protect.
Browser ── POST /login ──► tracenow Worker (on your zone) ──────────► your origin ▲ │ │ loads injected script │ 1. inject trace.min.js (first-party, on your domain) │ → /identify │ 2. is this a protected route? (GET requests are skipped) │ → device token │ 3. read email + device token + IP │ auto-attached to next │ 4. POST /trace → verdict │ request as header │ 5. deny → block here │ │ allow / challenge → forward to origin │ ▼ │ api.tracenow.io → enrich signals + run policies → allow | challenge | deny
Monitor mode: the Worker observes and logs the verdict asynchronously. Zero latency added
to the request.
Enforce mode: the Worker waits for the verdict synchronously (~100 ms) and returns a
configured block response when the verdict is deny.
Fail-open: if /trace is slow or unreachable, the request flows through to your origin.
The Worker never breaks your site.
The edge cannot render your app’s MFA flow or CAPTCHA, so a challenge verdict is
forwarded to your origin. Only deny blocks at the edge. Enforce challenge in-app via
the server-side call or your own step-up logic.
Before authentication the Worker has no subject_user_id because the user hasn’t proved who
they are yet. User-keyed signals (new device, impossible travel) fall back to the submitted
email as the account key. For full per-user depth, add the in-app call after
you authenticate the user.
In the dashboard, go to Integration and paste your
Cloudflare API token. tracenow validates the token and lists every zone on your account.
Your token is encrypted at rest.Select the zone(s) you want to protect. tracenow stores the connection and shows a badge
for each connected zone.
Integration: connected state with zone badges
📸 Screenshot to add: the Integration page right after connecting. Show the zone badges and the Deploy worker button.
2
Configure route rules
Under Routes, add a rule for each endpoint you want to protect. Each rule maps a
route pattern + HTTP method to an event type.
Field
Description
Route pattern
e.g. /auth/login, /api/v1/signup, /otp/send
Method
POST is the default; GET requests are never monitored
Event
login, signup, or otp
Mode
Monitor (observe only) or Enforce (act at the edge)
Start in Monitor mode. Let traffic flow for a day or two to see what your baseline
looks like before switching to Enforce.
tracenow suggests route patterns automatically based on common paths. You can accept the
suggestion or type your own.
3
Configure the blocked response (Enforce mode only)
In Enforce mode, when the verdict is deny, the Worker returns a response you control. The
default is a 403 with an empty body. You can override it per-route:
Option
How to configure
Best for
Status code only
Enter a status (e.g. 403, 429)
Simple API consumers
302 redirect
Enter a URL
Sending users to an error page
Custom HTML body
Paste an HTML page
Rendering a branded error to browsers
Custom JSON body
Paste a JSON object
API / XHR clients
tracenow content-negotiates: if the request Accept header prefers application/json and
you’ve configured both HTML and JSON bodies, the JSON body is returned.
4
Deploy
Click Deploy worker. tracenow pushes the Worker to Cloudflare and wires the route on
your zone. The deployment takes a few seconds.
Every time you change a route rule or the blocked response, click Deploy again to push
the updated config. Changes don’t take effect until you redeploy.
Make a real login or signup through your site. Within a few seconds it should appear in the
Events feed.
Event feed: a login trace with verdict
📸 Screenshot to add: the Events feed showing a recent login trace. Expand one row to show the signals panel (IP, device, verdict).Each trace shows the signals collected, which rule matched (if any), and the verdict. If you
see signals_complete: false on a trace, refresh after ~500 ms. Some async enrichment may
still be resolving.
In Monitor mode, rules still evaluate and verdicts are recorded. You just don’t block
anything. This lets you tune rules before they have any impact on real users.
See Rules for how to build and tune policies. See
Verdicts for the full allow / challenge / deny semantics.
The Cloudflare Worker can’t know which account is logging in: the user isn’t authenticated
yet. For signals like “new device for this account” or “impossible travel,” call /trace from
your backend after you verify credentials, with the real subject_user_id and confirmed
status.The device token is already on the request: because the injected script auto-attaches it to
same-origin requests, it arrives at your server in the x-tn-device-token header. Read it and
forward it.
// After your server has authenticated the userconst deviceToken = req.headers["x-tn-device-token"] as string | undefined;const trace = await fetch("https://api.tracenow.io/trace", { method: "POST", headers: { Authorization: `Bearer ${process.env.TRACENOW_SECRET_KEY}`, // tn_live_... "Content-Type": "application/json", }, body: JSON.stringify({ event: "login", status: loginSucceeded ? "succeeded" : "failed", subject_user_id: user.id, // the real account — the edge never has this email: user.email, ip: clientIp, device_token: deviceToken, // auto-arrived on the request }),}).then((r) => r.json());switch (trace.verdict) { case "allow": return issueSession(user); case "challenge": return requireMfa(user); case "deny": return res.status(403).json({ error: "Blocked" });}
# After your server has authenticated the userimport httpx, osdevice_token = request.headers.get("x-tn-device-token")trace = httpx.post( "https://api.tracenow.io/trace", headers={"Authorization": f"Bearer {os.environ['TRACENOW_SECRET_KEY']}"}, json={ "event": "login", "status": "succeeded" if login_succeeded else "failed", "subject_user_id": user.id, "email": user.email, "ip": client_ip, "device_token": device_token, },).json()
See Server-side integration for the full /trace contract, every
request field, and the complete response shape.
To remove the integration, go to Integration → Disconnect. tracenow removes the Worker
from your Cloudflare zone, deletes the route binding, and revokes the stored token. Existing
trace data is preserved.