PHP

The zeroad.network/token Composer package adds Zero Ad Network integration to any PHP 7.2+ application - Laravel, Symfony, plain PHP, or any other framework.

By the end of this guide your site will: - Send the X-Better-Web-Welcome header on every response (declaring publishership) - Verify each visitor’s subscription token locally, with no external network call - Expose a $tokenContext array to your controllers and templates

Prerequisites

Install

composer require zeroad.network/token

Initialize once at startup

Create the Site instance once - in a service container, bootstrap file, or global config - not inside a request handler.

use ZeroAd\Token\Site;
use ZeroAd\Token\Constants;

$site = new Site([
  'clientId' => 'YOUR_CLIENT_ID', // from zeroad.network/publisher/sites
  'features' => [
    Constants::FEATURE['CLEAN_WEB'],
    // Constants::FEATURE['ONE_PASS'], // add if you support paywall bypass
  ],
]);

Add middleware

In your framework’s middleware layer (or at the top of every request handler), do two things:

// 1. Inject your site's publisher header into the response
header("{$site->SERVER_HEADER_NAME}: {$site->SERVER_HEADER_VALUE}");

// 2. Parse the subscriber token - returns an array of feature flags
$tokenContext = $site->parseToken($_SERVER[$site->CLIENT_HEADER_NAME] ?? null);

parseToken verifies the token’s ED25519 signature locally. If no token is present, expired, or invalid, all flags are false. No special handling needed for non-subscribers.

Use the feature flags

$tokenContext is a flat array of booleans. Use them directly in your controllers and templates:

// Controller
$article = Article::find($id);

return view('article', [
  'content'    => $tokenContext['DISABLE_CONTENT_PAYWALL'] ? $article->fullContent : $article->excerpt,
  'showPaywall'=> !$tokenContext['DISABLE_CONTENT_PAYWALL'],
  'showAds'    => !$tokenContext['HIDE_ADVERTISEMENTS'],
]);
// API endpoint
header('Content-Type: application/json');
echo json_encode([
  'title'      => $article->title,
  'content'    => $tokenContext['DISABLE_CONTENT_PAYWALL'] ? $article->fullContent : $article->excerpt,
  'isPaywalled'=> !$tokenContext['DISABLE_CONTENT_PAYWALL'],
]);

Available flags

Flag When true What to do
HIDE_ADVERTISEMENTS Subscriber has Clean Web + site declares it Skip rendering ad blocks
HIDE_COOKIE_CONSENT_SCREEN Same Skip cookie consent banner
HIDE_MARKETING_DIALOGS Same Skip newsletter/promo popups
DISABLE_NON_FUNCTIONAL_TRACKING Same Skip loading third-party trackers
DISABLE_CONTENT_PAYWALL Subscriber has One Pass + site declares it Serve full content
ENABLE_SUBSCRIPTION_ACCESS Same Treat visitor as subscribed to your service

Full working example

A minimal plain PHP application with the integration wired up end-to-end:

index.php

<?php
declare(strict_types=1);

require_once __DIR__ . '/../vendor/autoload.php';

use ZeroAd\Token\Site;
use ZeroAd\Token\Constants;

$site = new Site([
  'clientId' => 'YOUR_CLIENT_ID',
  'features'  => [Constants::FEATURE['CLEAN_WEB'], Constants::FEATURE['ONE_PASS']],
]);

// Inject publisher header + parse subscriber token on every request
header("{$site->SERVER_HEADER_NAME}: {$site->SERVER_HEADER_VALUE}");
$tokenContext = $site->parseToken($_SERVER[$site->CLIENT_HEADER_NAME] ?? null);

// Route handling
$uri = $_SERVER['REQUEST_URI'];

if ($uri === '/') {
  // Use $tokenContext flags to tailor the page
  $showAds     = !$tokenContext['HIDE_ADVERTISEMENTS'];
  $fullContent = $tokenContext['DISABLE_CONTENT_PAYWALL'];

  echo '<pre>' . htmlspecialchars(json_encode($tokenContext, JSON_PRETTY_PRINT)) . '</pre>';

} elseif ($uri === '/api') {
  header('Content-Type: application/json');
  echo json_encode(['tokenContext' => $tokenContext]);

} else {
  http_response_code(404);
  echo 'Not Found';
}

Full runnable examples are in the GitHub repository.

Using with Laravel

In Laravel, create a middleware class and register it globally:

app/Http/Middleware/ZeroAdNetwork.php

<?php
namespace App\Http\Middleware;

use Closure;
use ZeroAd\Token\Site;
use ZeroAd\Token\Constants;

class ZeroAdNetwork
{
  private Site $site;

  public function __construct()
  {
    $this->site = new Site([
      'clientId' => config('services.zeroad.client_id'),
      'features'  => [Constants::FEATURE['CLEAN_WEB']],
    ]);
  }

  public function handle($request, Closure $next)
  {
    $clientHeader = strtoupper(str_replace('-', '_', $this->site->CLIENT_HEADER_NAME));
    $tokenContext = $this->site->parseToken($_SERVER["HTTP_{$clientHeader}"] ?? null);
    $request->merge(['tokenContext' => $tokenContext]);

    $response = $next($request);
    $response->headers->set($this->site->SERVER_HEADER_NAME, $this->site->SERVER_HEADER_VALUE);

    return $response;
  }
}

Then in a controller or Blade template:

$tokenContext = $request->input('tokenContext', []);

// Blade template
@if(!$tokenContext['HIDE_ADVERTISEMENTS'])
  {{-- render ad block --}}
@endif