Bounce Manager v2.1

Part of CloudWizard Email Suite

Bounce Manager connects to a bounce-handling mailbox over IMAP, classifies delivery failures as hard, soft, IP-block, or spam-reject, and automatically suppresses addresses that cross a configurable threshold — keeping your suppression list current without manual work.

Requirements

RequirementDetail
PHP8.1 or higher, with the imap extension enabled
MariaDB / MySQL5.7+ / 10.3+
Web serverNginx or Apache
A bounce mailboxAny IMAP-accessible mailbox that receives delivery failure notifications — dedicated address, shared inbox, or a folder/filter within one
Cron accessA way to trigger a URL or run a PHP script every 5-15 minutes

Step 1 — Install the Controller

Bounce Manager runs inside the CloudWizard Controller. The controller handles authentication, shared database config, and module detection. Install it first.

Upload the controller

Unzip the download. It holds one folder, CW/, with the controller and Bounce Manager inside. Upload that whole folder to a directory on your server — typically public_html/CW/. The controller part looks like this:

public_html/CW/
  bootstrap.php
  config_path.php
  vendor/            ← bundled PHPMailer, shared across every module
  admin/
    index.php
    login.php
    logout.php
    settings.php
    setup.php
    style.css
    ...

Run the setup wizard

Visit https://yourdomain.com/CW/admin/setup.php to run the wizard. Fill in:

  • Database — host, name, username, password, table prefix (default: cw_)
  • SMTP — outgoing mail server credentials and sender address
  • Admin password — minimum 8 characters
  • Site URL — your public domain

Click Complete setup. The config file is written outside your webroot where possible.

Log in

You're redirected to the login page. Enter your admin password to reach the CloudWizard dashboard.

Controller ready. Continue to install Bounce Manager.

Step 2 — Install Bounce Manager

Upload the module

The module is already inside the zip you just uploaded, at CW/BounceManager/. Adding it to an existing CloudWizard install instead? Don't upload the whole CW/ folder again: copy just this module's BounceManager/ folder into your existing controller directory and reload the dashboard. Its file layout:

public_html/CW/BounceManager/
  module.php
  bootstrap.php
  cron.php
  dmarc_import.php   ← one-off import of old DMARC report mails
  admin/
    _bootstrap.php
    index.php      ← Overview / Bounces / scan panel
    patterns.php
    settings.php
    _dmarc_mailboxes.php   ← DMARC mailbox form (shown on Settings)
    dmarc.php      ← DMARC reports tab
    audit.php
  lib/
    scan.php       ← IMAP scanning engine
    dmarc.php      ← DMARC report reader

Verify detection

Reload the controller dashboard. The Bounce Manager card should show Open →. Tables (accounts, patterns, bounces, audit log, settings) and a set of default classification patterns are created automatically on first load.

Step 3 — Activate your license

Whether you purchase through cloudwizard.eu or via Gumroad, enter your license key under the controller's Settings → License key. See the Audience Manager guide for the full walkthrough if needed.

Step 4 — Add an IMAP account

Go to Bounce Manager → Settings and fill in the Add Account form:

FieldNotes
LabelInternal name for this mailbox, e.g. bounces@example-shop.eu
IMAP HostYour mail server's IMAP hostname
Port143 (plain/STARTTLS) or 993 (SSL) — pair with the Connection setting below
MailboxUsually INBOX, or a specific folder if you filter bounces there
Username / PasswordFull mailbox credentials
ConnectionSTARTTLS (usually port 143), SSL/TLS (usually port 993), or None, plain (only when the mailbox is on the same server, host 127.0.0.1)

Click Test next to the saved account to confirm the connection before relying on it — it reports the message count in the mailbox or the exact IMAP error if it fails. The Status column shows the real state: Not tested, Connected, Can't connect or Off, plus when the last check ran.

The bounce address itself can be a dedicated mailbox, your main inbox, or a filtered folder within it — Bounce Manager only needs IMAP access to wherever delivery failures land.

Step 5 — Set up the cron job

Nothing scans automatically without this. Bounce Manager → Settings shows the exact cron key and URL for your install:

OptionWhen to use it
A — URL (wget)Works on any host with outbound HTTP access from cron; the URL includes a secret key so it can't be triggered by anyone else
B — PHP (direct)Runs the script directly via the PHP CLI — no key needed, since reaching it this way already requires shell access to the server

Add it under Virtualmin → Scheduled Cron Jobs, running every 5-15 minutes. A single cron run fully drains each account's backlog (not just one batch), looping until nothing remains — so even after a large bounce spike, one run catches everything up rather than trickling in over several cycles.

Overview

The Bounces tab (Bounce Manager's landing page) shows five stat cards — Total, Hard bounces, Soft bounces, IP blocks, Spam rejects — plus the scan panel and your five most recent scans.

Bounces & scanning

Run Scan (left panel) lets you pick which active accounts to scan, then click 🔍 Scan now. The cron job does the same thing automatically (it also fetches new DMARC reports); the button is for when you want the result right away. Scanning runs in chunks over AJAX with a live progress bar — safe for very large mailboxes, since each chunk is a separate short request rather than one long one that could time out.

The Bounce List (right panel) shows every classified bounce, filterable by type, source account, and email search. Select rows with the checkboxes and click 🗑 Delete to remove entries, or use the Export row to download a CSV — Exclude list (hard+soft combined), Hard only, Soft only, or All.

Processed messages are deleted from the IMAP mailbox after classification — the mailbox is meant as a transient inbox for bounces, not a permanent archive. The classified result lives in Bounce Manager's own bounce list instead.

Patterns

Classification runs against a pattern library you can fully edit under Patterns, seeded with sensible defaults on first install:

CategoryPurpose
Subject ruleMatched against the bounce email's subject line first — can resolve straight to hard/soft, or defer to a body scan (check) or delete unclassified (skip, e.g. auto-replies/out-of-office)
IP Block (body)Text fragments in the message body indicating the sending IP was blocklisted
Spam Reject (body)Text fragments indicating the message was rejected as spam/policy violation

Each pattern has a match type (Starts with, Contains, or Regex), a result type, a sort order (lower runs first), and an Active toggle — turn a pattern off without deleting it if it's causing false positives.

Settings

FieldNotes
Hard bounce thresholdNumber of hard bounces before auto-suppression. Default 1 — a hard bounce means the address is genuinely dead, so most installs suppress on the first one
Soft bounce thresholdNumber of soft bounces (mailbox full, temporary failure) before suppression. Default 3 — soft bounces are given more tolerance since they often self-resolve
Auto-suppressMaster toggle for the threshold behavior above. Turn off to classify and log bounces without ever touching the suppression list automatically
Cron keyThe secret protecting the cron URL — editable if you need to rotate it

IMAP accounts are also managed on this page — see Step 4.

DMARC reports

Big providers (Google, Microsoft, Mail.ru, Yahoo and others) send a daily summary of all mail they received in your domain's name: which server sent it, and whether it passed the DKIM and SPF checks. Bounce Manager → DMARC reads these reports and shows totals per domain and provider, your sending servers and how many messages passed, and a day-by-day list with each provider's decision. Mail that fails here is the usual reason for spam-folder delivery, and unknown server addresses can reveal someone forging your domain.

StepWhat to do
1. MailboxCreate a separate mailbox for the reports, e.g. dmarc@yourdomain.com. Do not reuse the bounce mailbox: the two kinds of mail are handled differently.
2. DNSIn the domain's _dmarc TXT record, add rua=mailto:dmarc@yourdomain.com. Use rua only (aggregate reports); ruf failure reports are not read and can contain message content.
3. ConnectSettings → DMARC report mailboxes: same form as the bounce accounts. Bounce accounts and DMARC mailboxes are listed separately.
4. WaitThe cron job fetches new reports each run. Reports arrive once a day per provider, so the tab fills within a day or two. Old report mails already in the mailbox can be imported once with php BounceManager/dmarc_import.php.

Reports are stored in compact form (zip, gzip and plain XML are understood), duplicates are ignored, and entries older than 400 days are removed automatically.

Audit Log

Three panels: an Account Summary (lifetime totals per source account, with scan count and last-scan time), a Scan History of the last 50 runs, and a Processing Failures log — messages that matched a bounce pattern but where no recipient email could be extracted from the body. Both the Scan History and Failures logs have a Clear button to wipe them if they grow large; this only clears the log, never the classified bounces themselves.

How classification works

Spam refusals never suppress an address. A refusal such as "550 spam message rejected", a 5.7.x policy code, or a block/DMARC/blocklist message says something about your sending reputation, not about the recipient. These are logged as Spam Reject (or IP Block) and the address stays on your list. Only real dead-address bounces (user unknown, mailbox disabled) and repeated soft bounces add an address to the suppression list. If Warm-up is on in Mail Engine, spam refusals also lower the suggested limit for that provider.

For each message: the subject is checked against Subject Rules first. If a rule resolves straight to a type, that's used; if a rule says check, the body is scanned next — for known SMTP status codes (5xx → hard, 4xx → soft), quota/mailbox-full phrases (→ soft), and known hard-failure phrasing (user unknown, account disabled, etc.). Every message is also independently checked against the IP Block and Spam Reject body patterns regardless of what the subject rule decided, so those two categories are never missed even if a message also happens to match a generic hard-bounce subject.

The recipient's email address is extracted from the bounce body using a cascade of methods, from most to least reliable: RFC 3464 Final-Recipient/Original-Recipient headers, VERP-style Return-Path addresses, Postfix-style inline addresses, keyword-based patterns ("failed recipient:", "delivered to:"), the X-Failed-Recipients header, the original message's To: header, and finally the first plausible non-system email address found anywhere in the body. If none of these succeed, the message is logged to Processing Failures rather than silently dropped.

Security

Troubleshooting

ErrorCauseFix
"Test" fails on an account Wrong host/port/SSL combination, or bad credentials Confirm port 993 pairs with SSL checked, port 143 with it unchecked; verify credentials directly with a mail client if unsure.
Scan finds nothing despite bounces sitting in the mailbox No Subject Rule matches the bounce format your mail server sends Check Patterns — add a rule matching the actual subject line format, or check Processing Failures for messages that matched but couldn't yield a recipient.
Real bounces aren't being suppressed Auto-suppress is off, or the threshold hasn't been reached yet (e.g. soft bounces under the default threshold of 3) Check Settings — confirm Auto-suppress is on and thresholds match your intent.
Cron runs but nothing changes No active accounts, or the cron key is wrong Confirm at least one account is marked Active in Settings, and the cron URL's key matches exactly.
Processing Failures log growing steadily Some bounce format used by your mail server doesn't match any of the recipient-extraction methods Check the Raw Snippet column for a pattern — this may need a custom pattern rule or is simply an edge case worth ignoring if rare.
Module not detected on dashboard module.php missing or not readable Confirm CW/BounceManager/module.php exists. Restart PHP-FPM: systemctl restart php8.3-fpm
License key not accepted Wrong key format or server can't reach cloudwizard.eu Key format: XXXX-XXXX-XXXX. Check outbound HTTPS: curl -s https://cloudwizard.eu/license/ping.php
500 error on admin pages DB error, missing table, or file permissions tail -20 /home/yourusername/logs/php_log

CloudWizard Email Suite — Bounce Manager v2.1
cloudwizard.eu — support@cloudwizard.eu