WordPress

The Zero Ad Network plugin connects your WordPress site and applies the subscriber experience through integrations with ad, consent, popup, paywall, and membership plugins.

The plugin is published in the WordPress.org plugin directory.

Before you start

You need WordPress 4.9+, PHP 7.2+ with the Sodium extension, and your Publisher ID: zapub_7Fq2xR9nKd3wV8mB4tL6yH1c (example Publisher ID). You can also copy it from Sites & creators. Sites are discovered automatically when the extension records a visit; there is no separate site registration step.

The plugin verifies Freedom membership. Use the Freedom access box in the post editor to choose which paid posts and pages subscribers can read.

Install and configure

  1. In WordPress, open Plugins > Add New Plugin, search for Zero Ad Network, then install and activate it. Alternatively, download the ZIP from WordPress.org and install it using Upload Plugin.
  2. Open Zero Ad Network > Settings in the WordPress admin menu.
  3. Paste your Publisher ID and select Enable Plugin.
  4. Leave Publisher Header Method set to HTTP Response Header unless your hosting requires the HTML Meta Tag option.
  5. Save your settings. If you sell access, edit each included post or page, check Freedom access > Include this content with Freedom, and save it. Offer your base subscription content or a custom selection that unlocks paid content or functionality.
  6. Clear existing page caches, then verify included and excluded content as both a subscriber and a regular visitor.

The plugin announces Better-Web-Publisher and verifies incoming Better-Web-Token headers locally. It derives the allowed hostnames from your WordPress and Site Address URLs, so those must match the public domains you serve.

What changes for subscribers

On a normal frontend page, a verified subscriber gets all of the supported integrations at once:

  • Ads and known non-essential tracking scripts are suppressed.
  • Cookie consent banners and marketing popups are removed or hidden.
  • Selected published posts and pages gain reading access through the supported membership hooks.
  • Unselected content, higher tiers, private posts, drafts, and password-protected posts keep their restrictions. The plugin does not grant purchases, site membership records, or administrative capabilities.

These changes apply to verified subscribers. Missing or invalid tokens keep the regular experience. The page transformations do not run on WordPress admin, AJAX, REST API, or JSON responses.

The plugin cannot cover every theme or custom ad placement. Use the compatibility reference below, then check actual pages, including protected posts and scripts loaded by your theme.

Page caches and CDNs

A page cache can serve HTML before WordPress runs, preventing the plugin from checking the token. Configure every caching layer to bypass cache reads and writes when the request includes Better-Web-Token, and forward that header to WordPress. This lets the plugin verify the visitor and keeps the clean response out of the shared cache.

The plugin marks token-bearing responses Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0, sends CDN/LiteSpeed no-store signals, and defines DONOTCACHEPAGE. Public responses remain cacheable and vary on Better-Web-Token to prevent a browser reusing the ordinary page after a token appears. It does not create subscriber variant cookies or cache entries. A supplied zeroad_variant cookie or X-ZeroAd-Variant header never grants access.

WordPress and server caches

PHP page caches can load through advanced-cache.php before regular plugins. In an installation already using WP_CACHE, replace that definition in wp-config.php, before WordPress loads:

$freedomTokenRequest = array_key_exists('HTTP_BETTER_WEB_TOKEN', $_SERVER);

define('WP_CACHE', !$freedomTokenRequest);

if ($freedomTokenRequest && !defined('DONOTCACHEPAGE')) {
    define('DONOTCACHEPAGE', true);
}

Do not add a second definition. This bypasses the PHP drop-in, not an upstream CDN or a web-server rewrite serving cached HTML. WordPress loads the drop-in early.

Cache Required configuration
WP Super Cache Use PHP/Simple delivery with the early bypass. Expert/rewrite delivery also needs a server exclusion before cached-file delivery.
WP Rocket, W3 Total Cache, WP Fastest Cache Apply the early bypass wherever the PHP drop-in is used. Configure any direct cached-file delivery separately. Automatic compatibility is not claimed.
LiteSpeed Cache Exclude token requests before server cache lookup. The plugin also marks origin responses non-cacheable using LiteSpeed's hook/header.
Apache, CloudFront, managed hosting Configure both lookup and storage bypass and forward the token header. Disable HTML caching on participating pages if these controls are unavailable.

WP Super Cache's Simple and Expert modes run at different layers. LiteSpeed documents its no-cache response hook. Neither makes a regular WordPress plugin run before an upstream cache hit.

Nginx

Add a map in http, then the matching directives to your existing caching location. Preserve other cache exclusions:

map $http_better_web_token $freedom_skip_cache {
    ""      0;
    default 1;
}

# proxy_cache location:
proxy_cache_bypass $freedom_skip_cache;
proxy_no_cache $freedom_skip_cache;
proxy_set_header Better-Web-Token $http_better_web_token;

# OR fastcgi_cache location:
fastcgi_cache_bypass $freedom_skip_cache;
fastcgi_no_cache $freedom_skip_cache;
fastcgi_param HTTP_BETTER_WEB_TOKEN $http_better_web_token;

The map ensures even an invalid token of 0 bypasses. Nginx treats an empty header like an absent one here; neither grants access. Both cache bypass and no-cache storage controls are necessary. Do not configure the cache to ignore private/no-store responses.

Varnish and Cloudflare

In Varnish, put this before cache lookup or early returns in vcl_recv, preserving the header to the backend:

if (req.http.Better-Web-Token) {
    return (pass);
}

For Cloudflare, use a Cache Rule with Cache eligibility: Bypass cache and this expression:

http.request.headers.truncated or has_key(http.request.headers, "better-web-token")

The header map uses lowercase names, so this handles mixed-case HTTP/1 headers too. Truncated header inspection also bypasses. Ensure other rules or Workers do not override the bypass or strip the token.

Acceptance checks

Clear all page caches after configuration or included-content changes. Check the same URL in this order:

  1. Without a token, warm the ordinary public cache.
  2. With a valid token and no cookies, confirm WordPress verifies it and unlocks only included content on the first visit.
  3. Repeat the valid request: it must still bypass lookup and storage, with a private/no-store response.
  4. Remove the token: the ordinary protected page must remain intact.
  5. Repeat with invalid, expired, and wrong-host tokens, plus forged zeroad_variant=subscriber1 cookies and X-ZeroAd-Variant headers. None may grant Freedom access. Existing WordPress membership permissions remain independent.

Inspect each cache layer's logs or status headers. A MISS alone is insufficient: it may still populate a cache. These are deployment checks; plugin unit tests do not certify third-party cache installations.

Expired tokens stop unlocking content, subject to the SDK's default 60-second clock tolerance. Cancellation or closure stops renewal but cannot revoke a signed offline token before expiry. Verification-result caching never extends a successful cached verdict beyond the token's expiry.

Token verification caching

Under Performance & Caching, Enable APCu Caching controls reuse of token verification results. With APCu available, results can survive between PHP requests; otherwise the plugin falls back to memory for the current request. Cache TTL (seconds) sets the verification cache duration.

This setting does not configure a CDN or full-page cache. Verification still works without APCu.

Check the integration

  1. Visit a public page and confirm it includes Better-Web-Publisher as the configured response header or meta tag.
  2. Once your site appears in the dashboard, open its page and use Test in your browser with the extension installed.
  3. Visit pages containing ads, cookie banners, and popups. Check that selected paid content opens, while unselected, private, draft, and password-protected content stays protected.
  4. Open the same URLs without the extension and confirm that the regular experience still works.

If the site is not detected, check that the plugin is enabled, the Publisher ID is correct, and old cached pages have been cleared. If subscribers still see ads or a paywall, first check that their requests reach WordPress with Better-Web-Token; then check the relevant plugin integration.

Supported plugins reference

The tables below describe the integrations targeted by the current plugin code. They use plugin hooks, script removal, and CSS depending on the integration. They are not a certification of every third-party plugin version: check your installed versions and theme with a subscriber visit.

Identifiers are the text domains the plugin checks for active plugins, unless a callback or filter is named. They are not necessarily WordPress.org download slugs. Custom markup and plugin updates can need additional integration.

Advertisements

Plugin Detection or hook
Ads For WP ads-for-wp
Ad Inserter ad-inserter
Advanced Ads advanced-ads
WP Quads quick-adsense-reloaded
AdRotate adrotate
Advanced Ads - Google AdSense In-feed advanced-ads-adsense-in-feed
AdWidget AdWidget_ callbacks
WP AdCenter wpadcenter
Corner Ad corner-ad
Ad Injection ad-injection
Simple Ads Manager simple-ads-manager
BuddyPress Ads buddypress-ads
Google Site Kit (AdSense) googlesitekit_adsense_tag_blocked filter

Raptive/AdThrive is not listed as confirmed support. Its existing suppression rule checks the cmb2 text domain before removing AdThrive_Ads callbacks, but CMB2 is a separate custom-fields toolkit. That rule does not reliably identify an active Raptive integration; verify your site's ads separately.

Plugin Detection or hook
Cookiebot cookiebot
Beautiful Cookie Consent Banner bar-cookie-consent
Real Cookie Banner real-cookie-banner
GDPR Cookie Consent gdpr-cookie-consent
Pressidium Cookie Consent pressidium-cookie-consent
WPConsent wpconsent-cookies-banner-privacy-suite
GDPR Cookie Compliance gdpr-cookie-compliance
Complianz | GDPR/CCPA Cookie Consent complianz-gdpr
CookieYes | GDPR Cookie Consent cookie-law-info
Cookie Notice & Compliance for GDPR / CCPA cookie-notice
CookieAdmin cookieadmin
Cookie Notice and Consent Banner cookie-notice-and-consent-banner
Cookies and Content Security Policy cookies-and-content-security-policy
Termly - GDPR/CCPA Cookie Consent Banner uk-cookie-consent
GDPR by Trew Knowledge gdpr
Cookie Consent Box by Supsystic gdpr-cookie-consent-by-supsystic

Marketing Dialogs & Popups

Plugin Detection or hook
Popup Maker popup-maker
OptinMonster optin-monster-api
Popup Maker WP SGPM callbacks
Popup by Supsystic popup-by-supsystic
PopupKit popup-builder-block
Popup Anything - A Marketing Popup popup-anything-on-click
Advanced Popups advanced-popups
Popup Box ays-popup-box
Depicter - Popup & Slider Builder depicter
FooBox Image Lightbox foobox-image-lightbox
MailOptin Lite mailoptin
Poptin poptin
Popup Builder popup-builder
Popups for Divi divi-popup
Hustle hustle
WP Popups Lite wp-popups-lite
Icegram Express email-subscribers
Convert Pro convertpro
Newsletter Popup newsletter-popup
Ninja Popups ninja-popups
Thrive Leads Thrive_Leads class and tve_leads_should_display filter

The Convert Pro rule checks convertpro and removes Convert_Plug callbacks. An additional cp_show_popup override is registered only when cp_v2_popup_enabled exists. Convert Pro and Convert Plus are separate products; this shared override does not establish full support for both.

Included subscription content

Explicitly selected posts and pages are supported through:

Plugin Content access hook
Paid Memberships Pro pmpro_has_membership_access_filter
WP-Members wpmem_block

The adapters use the post supplied by the membership plugin, so including one post does not unlock another. See the Paid Memberships Pro hook reference and WP-Members hook reference.

Other paywalls and custom subscription functionality require a content-specific integration. Freedom access applies only to included content through supported integrations. It does not create a site subscription or purchase, or bypass post passwords. A selection alone cannot unlock an unsupported plugin.

Custom included access

Custom templates can use \ZeroAd\WP\Actions\SubscriptionAccess::allowsPost($post) when deciding whether to show the selected paid content. This checks the verified subscriber context and the publisher's selection. Preserve any access the visitor already has through your own membership system:

$canRead = $existingAccess || \ZeroAd\WP\Actions\SubscriptionAccess::allowsPost($post);

To maintain a custom content group instead of selecting each post, use the zeroad_freedom_includes_post filter. This example includes a publisher-created category:

add_filter('zeroad_freedom_includes_post', function ($included, $post) {
    return $included || has_category('freedom-included', $post);
}, 10, 2);

The filter defines included content; it is not proof of membership. Use allowsPost() for the actual access decision. Private, draft, password-protected, and non-post/page content remains excluded. Map custom paid functionality separately in your application, without granting account-management or purchase permissions.

An already clean, unrestricted site needs only its Publisher ID. A paywalled site must configure and verify actual elevated access before treating the integration as complete. Clear page caches whenever the included selection or custom mapping changes.