Node, Bun & Deno

The official @zeroad.network/token package handles both halves for you: it builds your Better-Web-Publisher header and verifies the visitor token. It runs on Node, Bun, and Deno, and works with any HTTP framework.

Install

npm install @zeroad.network/token
# or: bun add @zeroad.network/token

Set it up once

Create a publisher at startup and reuse it for the life of the process. Pass your ID and every hostname you serve — the hostname list is a safety check, so a token can only ever verify against a domain you actually listed.

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", "www.example.com"]
})

Use it on every request

Send the header, then check the visitor. verify() returns { subscriber: true | false } — whether the visitor is a verified Zero Ad Network subscriber. Store that result as isZeroAdSubscriber and branch on it.

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()
})

Use that verified subscriber flag to remove ads, non-essential third-party tracking, cookie consent screens, and marketing popups (including newsletter prompts). Grant your base subscription or a custom level of paid content or functionality. Higher tiers may remain restricted; check the requested content against your included access level before rendering it:

if (isZeroAdSubscriber) {
  // Omit ads and interruptions; grant your included content access.
}

Notes

The SDK's result cache does not protect a shared HTML cache. Configure your CDN, proxy, and application page cache to bypass both lookup and storage for requests carrying Better-Web-Token, forward the token to your application, and mark personalised responses private and non-cacheable. Check ordinary and subscriber requests to the same URL with a warm cache so protected content cannot leak to other visitors.

  • Pass the actual hostname. Listing an apex also allows its www sibling, but signatures remain distinct. Omitting the hostname uses the single configured value; with multiple configured entries, omission throws. Preserve the public hostname through your proxy.
  • Results are cached by default, so repeat visits from the same subscriber don't re-run the cryptography. Pass cache: false to turn it off.
  • Nothing to hide? If your site is already clean and unrestricted you don't need verify() at all — sending the header (response.setHeader(...publisher.header)) is enough to earn.

Verify your integration end to end with Test in your browser on your site's dashboard page.