Skip to main content
A metered paywall lets a reader see a few free articles, then asks them to subscribe. The classic dodge is to reset the count: clear cookies, open an incognito window, and the meter starts over from zero. That works because most meters key off a first-party cookie, and a cookie is the one thing the reader controls. This tutorial meters on the Device ID instead, an identifier the reader cannot reset that way, and on the account for signed-in readers.

What is paywall bypass?

Paywall bypass is when a reader gets past a metered or subscription wall without paying, most often by resetting the free-view count. They clear cookies, open a private window or rotate their IP so the meter forgets them and starts over. Each reset mints a fresh cookie and therefore a fresh Visitor ID (one device plus one cookie), which is exactly why a cookie-keyed meter forgets the reader.

How ShieldLabs surfaces it

Every metered view is one identification, and each identification carries a Device ID that holds through those resets, so you count views per device on an identifier the reader cannot wipe. The request ID the snippet hands your page is the join key your backend uses to look up that view’s Device ID before it serves or walls the article. For a signed-in reader, the same identification also carries the hashed User HID you pass, so you can count per account as well. A cookie meter and a Device ID meter behave identically until the reader tries to game them. Then they diverge: The cookie is minted in the browser and lost the moment cookies are cleared, and the Visitor ID is one device plus one cookie, so it resets too. The Device ID holds, and metering on it closes the reset loophole. An IP is no steadier: a VPN, proxy or mobile carrier hands the same reader a fresh address on demand. ShieldLabs holds the count to the device, and you choose where the wall sits.
This is a metering policy. A reader who hits the wall after their free views has read what the plan allows. The Device ID keeps the count honest through cookie resets, and you set the limit and what the wall says.

Stop paywall bypass

Read the Device ID off the webhook for every article view. The rule to apply: increment a per-device counter, compare it against your free limit, and show the wall once a device passes the limit; for a signed-in reader, count per account too. The outcome is a meter the reader cannot reset by clearing cookies, opening incognito or rotating their IP, because all three keep the same Device ID.

Build it

1

Create a ShieldLabs account and get your keys

Start Free with 5,000 identifications, one time, no credit card, or log in. In the analytics dashboard, add the domain you want to protect under Integration > Domains, then open Integration > API keys and copy its keys with the copy button next to each. The Public Key loads the snippet in the browser. Keep the server credentials on your backend: the Private API Key reads the History API, and each webhook endpoint has its own whsec_… signing secret. See API keys and Integration.
2

Identify on every article view

Load the snippet on every metered article page and call forceCheckAnonymous on each view. Within one visit (while a page of your site stays open in the browser, across route changes in a single-page app and across open tabs), checkAnonymous and checkAuthenticatedUser run at most one identification every five minutes for the same user. A call inside that window posts nothing, counts nothing, and its onInitialized handler receives { status: "not_initialized" }. On a multi-page site, a full page load in the only open tab can start a new visit with its own identification. A reader who keeps another tab of your site open, or moves between articles in a single-page app, stays in one visit, so a plain call on the next article would reach your backend with no request ID. forceCheckAnonymous runs an identification every time, keeps the current Session ID and restarts the five-minute window. Each metered view is one identification against your included identifications. Identification requests are rate-limited per visitor IP and per domain (see Rate limits). Readers behind one office or campus IP share the per-IP limit; once it is reached, identifications from that IP carry the 999 marker until the ban clears, and the handler below meters those views by cookie rather than by the shared IP.
article.html
On signed-in pages, call mod.forceCheckAuthenticatedUser(hashedAccountId, { onInitialized }) with the same callback instead, passing a hashed or pseudonymous account id, never a raw email.
3

Count per Device ID and wall at your free limit

Your backend reads the identification for that request ID from the shared waitForScore helper, then bumps the counter for the Device ID it carries and compares it against your free limit. Because clearing cookies and opening incognito both keep the same Device ID, the reader cannot zero the count by resetting browser storage.
api/meter.js
Metering reads the Device ID straight off the webhook, which never counts against your included identifications; each forceCheckAnonymous call is one identification. If a webhook is dropped (delivery is at-most-once, no retries), waitForScore falls back to a History API read by request_id, so a dropped webhook does not leak a free view. NIL_DEVICE is the all-zero Device ID, exported by the shared helpers.In the analytics dashboard, a device card lists its Linked visitors: one Device ID with a Visitor ID for every cookie reset, which is the reset loophole the device meter closes.
The device card for Device ID d290f1ee-6c54-4b01-90e6-d701748f0851 in the analytics dashboard with Linked visitors open: 9 Visitor IDs, each with its identifications and band.The device card for Device ID d290f1ee-6c54-4b01-90e6-d701748f0851 in the analytics dashboard in the dark theme with Linked visitors open: 9 Visitor IDs, each with its identifications and band.

One device and the Visitor IDs its cookie resets created, in the analytics dashboard.

4

Fall back for views the Device ID meter cannot cover

Some views carry no usable Device ID, and pretending otherwise leaks free views:
  • JavaScript off, or the snippet blocked or not loaded. The snippet cannot run, so your page gets no request ID and no webhook arrives. Count the view by cookie or IP where you serve the article.
  • An all-zero Device ID. An all-zero Device ID (00000000-0000-0000-0000-000000000000) means no usable device signals reached ShieldLabs for that identification; the rate-limit marker (Risk Score 999) is one such case. Meter that view by cookie or IP as well, rather than serving it free; meter the rate-limit marker by cookie only, since it can land on many readers behind one shared IP.
Headless and automated browsers that do run the snippet get a normal Device ID and raise the javascript_disabled or browser_automation risk signal, so they count on the device meter like any other reader. To wall them outright, check risk.detection_flags?.browser_automation before you count.The fallback meter keys on the IP your own server sees for the request, since these views carry no identification. For views that do carry one, the Local IP (local_ip.ip, the address the browser itself reports) can stay put while a VPN hands out a fresh public IP on demand, so it is the steadier per-network key when present.The other limit is by design: the Device ID belongs to one browser on one device, so the same reader on Chrome and on Firefox is two Device IDs, and a determined reader can earn fresh free views by switching browsers or machines. That is a far higher bar than clearing cookies. To raise it further, pair the per-device counter with a per-IP cap and a soft cookie meter, and require a free account for continued access once any of them trips. A signed-in reader is then metered per account, as the next step shows.
5

Meter signed-in readers by account

A signed-in reader is an account, and an account can reach you from several devices. On signed-in pages, call forceCheckAuthenticatedUser with the hashed User HID, and count free views per account as well as per device, so a reader cannot earn fresh views by switching browsers while signed in.
For subscribers, the Account sharing event below covers the other half: one account used from several distinct devices.
6

Tune to your product

Start by logging the per-device and per-account distribution of your real readers before you enforce, so your free limit matches how people actually read rather than a guess.
To catch readers who slip the per-device meter by opening fresh free accounts or sharing one subscription, pass the hashed User HID with forceCheckAuthenticatedUser on signed-in article pages, as the meter above does. ShieldLabs detects Multi-accounting on your users directly: several accounts run by one person, linked through the devices and network they share, the shape of a reader opening fresh free accounts. It also detects Account sharing: one account used from several distinct devices, the shape of one subscription passed around a household or a team. Each carries Medium or High confidence. High-Risk Events are available in the analytics dashboard, the API and webhooks, and need the hashed User HID you pass on signed-in pages. The Risk Score and risk signals of the identification remain the input when you serve or wall an article. When one of these events arrives for a reader through the API or webhooks, or when you review it in the analytics dashboard, act on the account. You choose the action for each case, for example applying your sharing policy at its next sign-in. A reader who switches to an anti-detect browser to look new without signing in raises the Anti-detect Browser risk signal instead. On the account’s user card in the analytics dashboard, the Account sharing pill’s colour gives its confidence (red for High, orange for Medium, the colours of the Dangerous and Suspicious bands, though the pill is an event), and Linked devices lists each device with the band of the account’s identifications on it.
The user card for User HID c47a1e90b3d25f18 in the analytics dashboard: the Trusted band pill, a red Account sharing pill (High confidence), 14 identifications, all Trusted, and Linked devices open with 5 devices, each Trusted.The user card for User HID c47a1e90b3d25f18 in the analytics dashboard in the dark theme: the Trusted band pill, a red Account sharing pill (High confidence), 14 identifications, all Trusted, and Linked devices open with 5 devices, each Trusted.

An account with an Account sharing event in the analytics dashboard, with the devices linked to it.

Test it

Confirm the meter holds before you enforce. Read past your free limit in a normal window, then try the resets a reader would try:
1

Clear cookies and reload

Read enough articles to trip the wall, clear cookies, and reload. The device_id in the webhook is the same one, so your per-device count keeps climbing instead of resetting to zero.
2

Open the article in incognito

Open the same page in a private window. The cookie_id and visitor_id change, but the device_id returns identical: your meter keys off the durable handle.
3

Switch to a second browser

Open the article in a different browser on the same machine. Here the device_id does change, because another browser is another Device ID. That is the honest limit: the per-IP cap slows it, and signing in brings the account meter into play.
Test as a real reader resetting their own browser. An automated client raises the Browser Automation risk signal, and a fetch with scripts off never runs the snippet, so neither exercises the device meter.
A guide, not a rule. The right limit depends on your content and your conversion goals.

Where to go next

To understand why the Device ID holds through cookie clears and incognito while the Visitor ID does not, read Identifiers, and Users, devices, visitors and IPs shows how an account links to the devices it reads from. The risk signals reference covers the masking and automation signals that travel alongside the Device ID. The exact device_id field and the rest of the identification live in the webhook payload your meter reads. The Billing page covers the included identifications on each plan. The same Device ID powers neighboring playbooks: Ban Enforcement keys a banlist on the device a returning reader cannot wipe, and Returning Visitor recognizes a known account on a device it has used before. To grade where your readers come from, Measure Traffic Quality grades each acquisition source by the risky users, devices and traffic it brings.