Subscribe

Newsletter subscription handler with lists, double opt-in, honeypot, rate limiting and unsubscribe link.

Newsletter subscription module for ProcessWire with multiple lists, double opt-in, honeypot spam protection, rate limiting, unsubscribe links, hookable events, and a full admin interface.

Features


  • Multiple subscription lists with many-to-many subscriber relationships
  • Double opt-in with configurable HTML confirmation email
  • Honeypot field that traps bots without CAPTCHA
  • IP-based rate limiting with configurable threshold and time window
  • One-click unsubscribe endpoint with unique token per subscription
  • Hookable subscribed event for integrating with other modules
  • WireMail provider selection (default, SMTP, Brevo, etc.)
  • Admin UI with sidebar list switcher, search/filter, status filter, pagination
  • Import subscribers from CSV
  • Export subscribers per list in JSON and CSV formats
  • Resend confirmation email from admin for pending subscribers
  • Public PHP API for use in order forms, contact forms, or any template
  • Auto-migration from legacy single-table schema

Requirements


  • ProcessWire 3.x
  • PHP 8.0+
  • Alpine.js on the frontend (for the included form block)

Installation


  1. Copy the Subscribe directory to /site/modules/Subscribe/
  2. In PW admin go to Modules > Refresh, then install Subscribe
  3. ProcessSubscribe installs automatically and adds a Subscribers page under Setup

The module creates four database tables and two confirmation pages on install. See Database schema and Confirmation pages.

Configuration


Go to Modules > Subscribe to configure:

Send method -- select WireMail provider (defaults to site setting). For SMTP or transactional providers, install the appropriate WireMail module first.

Confirmation email -- sender identity and email content:

  • From Email (defaults to $config->adminEmail)
  • From Name (defaults to hostname)
  • Subject line
  • Email body (HTML) with placeholders {confirm_url} and {unsub_url}. Leave empty to use the built-in default template.

Redirect pages -- pages shown after email confirmation. Auto-created on install, or select any page from the site tree.

Rate limiting -- max subscription attempts per IP within a time window (default: 3 attempts per 10 minutes).

Frontend form


The module ships with subscribe-form-block.php, an Alpine.js form component with honeypot protection and inline success/error messages.

Include it in any template:

<?php include $config->paths->siteModules . 'Subscribe/subscribe-form-block.php'; ?>

To target a specific list by ID:

<?php $subscribeEndpoint = '/?subscribe=2'; ?>
<?php include $config->paths->siteModules . 'Subscribe/subscribe-form-block.php'; ?>

The form posts to /?subscribe=1 by default. Alpine.js must be loaded on the page.

PHP API


subscribe($email, $listId = null)

Subscribe an email address. Handles validation, rate limiting, duplicate checks, and sends the confirmation email. Returns an associative array with success (bool) and message (string).

$sub = $modules->get('Subscribe');

$result = $sub->subscribe('user@example.com');

// Subscribe to a specific list
$result = $sub->subscribe('user@example.com', 2);

getSubscribers($listId, $status = 'active')

Retrieve subscribers for a list. Returns an array of associative arrays with keys: id, email, ip, status, created_at.

$active  = $sub->getSubscribers(1);
$pending = $sub->getSubscribers(1, 'pending');
$all     = $sub->getSubscribers(1, 'all');

resendConfirmation($subscriptionId)

Regenerate tokens and resend the confirmation email for a pending subscription. Returns true on success, false if the subscription is not found or not pending.

$sub->resendConfirmation($subscriptionId);

Hooks


The module fires a hookable subscribed event after a new subscription is created. Use it in /site/ready.php or any module:

$wire->addHookAfter('Subscribe::subscribed', function(HookEvent $event) {
    $email          = $event->arguments(0);
    $listId         = $event->arguments(1);
    $subscriptionId = $event->arguments(2);

    // Send Telegram notification, log to a page, sync with CRM, etc.
});

Database schema


Four tables are created on install:

TablePurpose
subscribe_form_listsNamed subscription lists
subscribe_form_subscribersUnique email addresses with IP and timestamp
subscribe_form_subscriptionsMany-to-many: subscriber to list with status, confirm token, and unsubscribe token
subscribe_form_ratelimitIP-based attempt tracking with automatic cleanup

Confirmation flow


  1. User submits email via the form or PHP API
  2. Subscriber record created (or found), subscription set to pending
  3. Confirmation email sent with unique token link
  4. User clicks /?subscribe_confirm=TOKEN
  5. Token matched, status set to active, token cleared
  6. User redirected to the success page

Subscription statuses:

StatusMeaning
pendingConfirmation email sent, awaiting click
activeConfirmed and receiving emails
unsubscribedOpted out via link or toggled in admin

Confirmation pages


On install, the module creates two pages with their templates:

PageTemplatePurpose
/subscribe-confirmed/subscribe-confirmedShown after successful email confirmation
/subscribe-error/subscribe-errorShown when token is invalid, expired, or already used

To use custom pages, go to Modules > Subscribe > Redirect pages and select any page. Edit the template files at /site/templates/subscribe-confirmed.php and /site/templates/subscribe-error.php to customize content.

Both pages and templates are removed on uninstall.

Admin UI


The ProcessSubscribe module adds a Subscribers page under Setup with:

  • Sidebar list switcher with subscriber counts
  • Create, rename, and delete lists
  • Add subscriber directly (inserted as active, skips opt-in)
  • Toggle subscriber status between active and unsubscribed
  • Remove subscriber from a list
  • Resend confirmation email for pending subscribers
  • Search/filter by email
  • Status filter dropdown (All / Active / Pending / Unsubscribed)
  • Pagination at 50 subscribers per page
  • Import subscribers from CSV file
  • Export per list in JSON and CSV formats

Export


Use the JSON and CSV buttons in the admin UI. Each export is scoped to the currently selected list.

Uninstall


Uninstalling removes all four database tables, both confirmation pages, and their templates. Export your data first.

License


Copyright (c) Maxim Semenov. Licensed under the MIT License.

https://smnv.org | maxim@smnv.org

More modules by Maxim Semenov

  • Context

    Export ProcessWire site context for AI development (JSON + TOON formats)
  • WireWall

    Advanced traffic firewall with VPN/Proxy/Tor detection, rate limiting, and JS challenge
  • LQRS URL Shortener Profile

    This site profile for ProcessWire offers a free and easy-to-use URL shortener that transforms long links into concise, shareable URLs. It is built using standard ProcessWire modules and field types.
  • Media Platform Profile

    This site profile for ProcessWire offers a fully-featured media platform for hosting and managing video content with Backblaze B2 and Cloudflare integration.
  • Plausible Analytics

    Plausible Analytics dashboard using Stats API v2 with page-edit widget, traffic trends chart, and geo/device tabs.
  • AiWire

    AI integration for ProcessWire. Supports Anthropic, OpenAI, Google, xAI, and OpenRouter.
  • Rapid

    EditorJS block editor fieldtype for ProcessWire. Stores content as JSON, renders HTML server-side via pluggable block renderers.
  • Page Markdown

    Export any page to a clean Markdown file. Adds an export button to the page editor.
  • Subscribe

    Newsletter subscription handler with lists, double opt-in, honeypot, rate limiting and unsubscribe link.

All modules by Maxim Semenov

Install and use modules at your own risk. Always have a site and database backup before installing new modules.