Node, Bun & Deno

Use the official @zeroad.network/token package to announce your Publisher ID and recognise subscribers. It works with any HTTP framework.

In short

  • You need: Node, Bun or Deno, your Publisher ID, and the list of hostnames you serve.
  • You do: install the package, add one middleware, and branch on isZeroAdSubscriber.
  • Done when: a test subscriber gets the clean page, and everyone else gets your normal one.

1. Install

npm install @zeroad.network/token

With Bun, run bun add @zeroad.network/token. With Deno, run deno add npm:@zeroad.network/token.

2. Create the publisher once

Create it at startup, and reuse it for the life of the process.

This is an example Publisher ID. Replace it with your own before using it.

import { createPublisher } from "@zeroad.network/token"

export const publisher = createPublisher({
  publisherId: "zapub_7Fq2xR9nKd3wV8mB4tL6yH1c",
  hostnames: "example.com", // or ["example.com", "blog.example.com"]
})

List every hostname you serve in hostnames. A token only verifies on a hostname in this list. Listing example.com also allows www.example.com, and the other way round.

3. Check every request

Add one middleware. It sends your header, checks the visitor, and stores the answer as isZeroAdSubscriber.

Hono

app.use(async (c, next) => {
  c.header(...publisher.header)

  const visitor = await publisher.verify(
    c.req.header(publisher.tokenHeaderName),
    c.req.header("host"),
  )

  c.set("isZeroAdSubscriber", visitor.subscriber)
  await next()
})

Express

app.use(async (req, res, next) => {
  res.setHeader(...publisher.header)

  const visitor = await publisher.verify(
    req.headers[publisher.tokenHeaderNameLowercase],
    req.headers.host,
  )

  res.locals.isZeroAdSubscriber = visitor.subscriber
  next()
})

verify() never throws on a bad token. A missing, broken, expired or wrong-site token simply returns subscriber: false.

4. Serve subscribers the clean page

Where you render ads, trackers, cookie banners and popups, skip them for subscribers:

if (!isZeroAdSubscriber) {
  // Render ads, cookie consent, non-essential trackers and marketing popups.
}

If you sell access, unlock your base subscription or an included level of paid content for subscribers. Higher tiers can stay restricted. Check the requested content against your included level before rendering it.

5. Keep subscriber pages out of shared caches

The SDK caches verification results, not your pages. If a CDN, proxy or page cache sits in front of your app, set it to bypass the cache for requests that carry Better-Web-Token, so they always reach your app.

Set up page caching and CDNs →

6. Check it works

  1. Confirm a normal page response includes Better-Web-Publisher.
  2. Open your website's page in your dashboard, and select Test in your browser.
  3. Reload your website. You should see the clean page and your included content.
  4. Open the same URL in a browser without the extension, with your caches warm. You should see the normal page.

Troubleshooting

Log visitor.reason to see why a token was rejected, and visitor.hostname to see which hostname was checked.

reason What it usually means
missing No token arrived. Most visitors are not subscribers. For a test subscriber, check that your proxy forwards Better-Web-Token, and reload after the first visit.
unknown_hostname The hostname isn't in hostnames. Add it to your configuration.
wrong_hostname The token was made for a different hostname. Check that your proxy keeps the public Host header.
expired The token is out of date. The extension refreshes tokens on its own.
unsupported_version The token is newer than your SDK. Check for an SDK update.

Good to know

  • Pass the real hostname. example.com and www.example.com have different signatures. If you configure several hostnames, you must pass one to verify().
  • Results are cached by default, so returning subscribers don't repeat the cryptography. Pass cache: false to turn it off.
  • Already-clean website? You don't need verify(). Sending the header, with response.setHeader(...publisher.header), is enough to earn.