Overview
This tutorial walks through implementing Fingerprint to enforce paywalls, ensuring that visitors cannot bypass limits or access premium content by clearing cookies, changing IP addresses, or using incognito mode. You’ll begin with a starter app that includes a mock news site with sample articles and a basic paywall flow that limits the number of articles you can read for free. From there, you’ll add the Fingerprint JavaScript agent to identify each visitor and use server-side logic with Fingerprint data to track article views and block further access once the limit is reached. By the end, you’ll have a sample app that reliably enforces paywall limits per visitor and can be customized to fit your subscription or content access model. This tutorial uses just plain JavaScript and a Node server with SQLite on the back end. For language- or framework-specific setups, see our quickstarts.Estimated time: < 15 minutes
Prerequisites
Before you begin, make sure you have the following:- A copy of the starter repository (clone with Git or download as a ZIP)
- Node.js (v20 or later) and npm installed
- Your favorite code editor
- Basic knowledge of JavaScript
1. Create a Fingerprint account and get your API keys
- Sign up for a free Fingerprint trial, or log in if you already have an account.
- After signing in, go to the API keys page in the dashboard.
- Save your public API key, which you’ll use to initialize the Fingerprint JavaScript agent.
- Create and securely store a secret API key for your server. Never expose it on the client side. You’ll use this key on the backend to retrieve full visitor information through the Fingerprint Server API.
2. Set up your project
- Clone or download the starter repository and open it in your editor.
Terminal
- This tutorial will be using the
paywallfolder. The project is organized as follows:
Project structure
- Install dependencies:
Terminal
- Copy or rename
.env.exampleto.env, then add your Fingerprint API keys:
Terminal
- Start the server:
Terminal
- Visit http://localhost:3000 to view the mock news homepage. Click three different articles to read them. Notice that you cannot read any new articles after that.
- The demo stores articles read data in localStorage to enforce the paywall. To reset the demo, either click Reset demo at the bottom of the page or manually clear the browser storage (e.g., open DevTools → Application → Local Storage → delete the site entry, or run
localStorage.clear()in the console), then reload and read more articles. You’ll see the paywall is easily bypassed when client-side storage is cleared.
3. Add Fingerprint to the front end
In this step, you’ll load the Fingerprint client when the page loads and trigger identification when the user goes to read an article. The Fingerprint client returns both avisitorId and a requestId. The visitor ID can be used to identify a browser or device uniquely and can be used to enforce the paywall. Instead of relying on the visitorId returned on the client side directly, you’ll send the requestId to your server along with the request for the article content. The server will then call the Fingerprint Events API to securely retrieve the full identification details, including the visitor ID, bot detection, and other signals.
- Before adding any new code, let’s delete all the client-side paywall logic and localStorage tracking. Specifically, open the
public/article.jsfile and remove theFREE_ARTICLES_LIMITconstant and these functions:getArticlesRead(),updateFreeCount(), andupdateArticlesRead(). - In
getArticle(), delete the block that checks the number of articles read and toggles the paywall. Keep only the line that gets thearticleIdand everything after thefetchcall.
public/article.js
- At the bottom of the file, remove the calls to
updateArticlesRead()andupdateFreeCount(), leaving only thegetArticle()call. - Now we’re ready to bring in Fingerprint. At the top of
public/article.js, load the Fingerprint JavaScript agent:
public/article.js
- Make sure to change
regionto match your workspace region (e.g.,eufor Europe,apfor Asia,usfor Global (default)). - Inside the
getArticle()function, request visitor identification from Fingerprint using theget()method. Then, update thefetchcall to use aPOSTrequest and include the returnedrequestIdin the body when sending the article request to the server. (A matchingPOSTendpoint is already set up for this tutorial.)
public/article.js
get() method sends signals collected from the browser to Fingerprint servers, where they are analyzed to identify the visitor. The returned requestId acts as a reference to this specific identification event, which your server can later use to fetch the full visitor details.
For lower latency in production, check out our documentation on using Sealed Client Results to return full identification details as an encrypted payload from the get() method.
- Update the “free articles remaining” message using the value returned from the server. (This won’t work yet — we’ll connect it in the next steps.)
public/article.js
4. Receive and use the request ID to get visitor insights
Next, pass therequestId through to your back-end logic, initialize the Fingerprint Server API client, and fetch the full visitor identification event so you can access the trusted visitorId and Smart Signals.
- In the back end, the
server/server.jsfile already defines API routes for the app. Update the/api/article/:idPOSTroute to also extractrequestIdfrom the request body and pass it into thegetArticlefunction. (You can delete the/api/article/:idGET route.)
server/server.js
- The
server/articlesApi.jsfile contains the logic for fetching articles. Start by importing and initializing the Fingerprint Server API client there, and load your environment variables withdotenv.
server/articlesApi.js
- Make sure to change
regionto match your workspace region (e.g.,EUfor Europe,APfor Asia,Globalfor Global (default)). - Update the
getArticlefunction to also acceptrequestIdand use it to fetch the full identification event details from Fingerprint:
server/articlesApi.js
requestId, the Fingerprint server client will retrieve the full data for the visitor identification request. The returned object will contain the visitor ID, IP address, device, and browser details, and Smart Signals like bot detection, browser tampering detection, VPN detection, and more.
You can see a full example of the event structure and test it with your own device in our demo playground.
For additional checks to ensure the validity of the data coming from your front end, view how to protect from client-side tampering and replay attacks in our documentation.
5. Enforce paywall limits by visitor ID
Next, use the trustedvisitorId to see how many free articles a visitor has read and enforce paywall limits. This ensures that even if someone clears cookies, uses incognito mode, or switches networks, their access is still tied to the same browser or device.
Note: The starter app includes a SQLite database with the following table already created for you:
SQLite database tables
- Add some helper functions to the bottom of the
server/articlesApi.jsfile to record and check articles read by the visitor:
server/articlesApi.js
- Update
getArticleto retrieve thevisitorId, from theeventobject and use it to determine how many articles the visitor has read usinggetArticlesRead. Make sure to includearticlesRemainingin the responses as well:
server/articlesApi.js
- Then add new paywall logic to check if the visitor can read the article. If so, log the article as being read and return its contents, otherwise block the article request:
server/articlesApi.js
6. Block bots and suspicious devices
This optional step uses the Bot Detection and Suspect Score Smart Signals,
which are only available on paid plans.
event object also includes the Bot Detection Smart Signal, which identifies automated activity so you can prevent bots from accessing your articles.
This signal returns good for known bots like search engines, bad for automation tools, headless browsers, or other signs of scraping, and notDetected when no bot activity is found.
- Continuing in the
getArticlefunction inserver/articlesApi.js, check the bot signal in theeventobject and block requests from bots:
server/articlesApi.js
- Below the bot detection check, add a condition that reads the Suspect Score from the
eventobject and blocks the article request if it exceeds a chosen threshold (for example, 20):
server/articlesApi.js
This is a minimal example to show how to implement Fingerprint. In a real
application, make sure to apply proper security practices, error handling, and
paywall logic that align with your production standards.
7. Test your implementation
Now that everything is wired up, you can test the full protected paywall flow.- Start your server if it isn’t already running and open http://localhost:3000:
Terminal
- Read three different articles and on your fourth unique article you should hit the paywall.
- Open a private browser window, visit the site, and try to read another new article beyond the limit. You will still be paywalled.
- Bonus: Test the flow using a headless browser or automation tool to see bot detection in action. A sample script is available in
test-bot.js. While your app is running, run the script withnode test-bot.jsin your terminal and observe that the automated article requests are denied.
Terminal