CloudWizard IMAP Bounce Manager
This guide covers installation, configuration, and daily use of the CloudWizard IMAP Bounce Manager. The tool scans your email inboxes for bounce messages, classifies them, and builds a clean exclude list for your mailing campaigns.
Single-file tool. The entire application is one PHP file (imapbm.php) plus one optional helper script (update_hard_bounces.php). There are no dependencies, no Composer packages, and no build steps.
Requirements
| Component | Requirement | Notes |
|---|---|---|
| PHP | 8.1, 8.2, or 8.3 | 8.0 may work but is unsupported |
| PHP extension | php-imap | See install instructions below |
| Database | MariaDB 10.3+ or MySQL 8+ | Tables are created automatically |
| Web server | Nginx or Apache | Including shared cPanel/Plesk hosts |
| IMAP access | Port 143 or 993 | Must be accessible from your server |
Installing the PHP IMAP extension
Ubuntu / Debian (Nginx + PHP-FPM)
apt install php8.3-imap
systemctl restart php8.3-fpm
cPanel shared hosting
Go to cPanel โ Select PHP Version โ Extensions and enable imap. Save and apply.
Plesk
Go to Tools & Settings โ PHP Settings, select your PHP version, and enable the imap extension.
Verify the extension is loaded
php -m | grep imap
Should output: imap
Database Setup
Create a dedicated database and user for the bounce manager. Log into MySQL/MariaDB as root:
mysql -u root -p
CREATE DATABASE bounce_handler CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER 'bounce_user'@'localhost' IDENTIFIED BY 'YourStrongPassword'; GRANT ALL PRIVILEGES ON bounce_handler.* TO 'bounce_user'@'localhost'; FLUSH PRIVILEGES; EXIT;
All database tables are created automatically when you load the tool for the first time. You do not need to run any SQL schema files.
Upload & Configure
Upload imapbm.php
Upload imapbm.php to a directory on your server. We recommend a protected subdirectory such as /var/www/yourdomain.com/tools/ or a subdomain like bounce.yourdomain.com.
Edit the DB credentials
Open imapbm.php in a text editor and update the four constants at the top of the file:
define('DB_HOST', 'localhost'); define('DB_NAME', 'bounce_handler'); define('DB_USER', 'bounce_user'); define('DB_PASS', 'YourStrongPassword');
Nginx Configuration
Add a location block with HTTP basic authentication. Create a password file first:
apt install apache2-utils # provides htpasswd
htpasswd -c /etc/nginx/.htpasswd admin
Then add to your Nginx server block:
location /tools/ {
auth_basic "CloudWizard";
auth_basic_user_file /etc/nginx/.htpasswd;
# Increase upload limit for the DB updater CSV
client_max_body_size 20M;
}
nginx -t && systemctl reload nginx
The tool stores IMAP passwords in your database. Always run it over HTTPS and behind authentication.
Apache Configuration
Create a .htaccess file in the same directory as imapbm.php:
Options -Indexes AuthType Basic AuthName "CloudWizard" AuthUserFile /path/to/.htpasswd Require valid-user # Increase upload limit for the CSV updater php_value upload_max_filesize 20M php_value post_max_size 20M
Create the password file:
htpasswd -c /path/to/.htpasswd admin
Make sure AllowOverride AuthConfig is set in your Apache virtual host, or AllowOverride All. On cPanel hosts this is typically already enabled.
PHP upload limits on shared hosting
If you cannot use .htaccess to set PHP limits, look for a php.ini or user.ini file in your home directory:
upload_max_filesize = 20M post_max_size = 20M
First Run
Open the tool in your browser. On first load it will:
- Create the
imap_accounts,bounces,bounce_patterns,bounce_failures, andaudit_logtables - Seed 20+ default bounce patterns (subject rules, IP block keywords, spam reject keywords)
You should see the Bounces tab with empty stats and a prompt to add your first account in Settings.
If you see the interface with zero counts, installation is complete. Proceed to adding your IMAP accounts.
Overview
The IMAP Bounce Manager has four tabs:
| Tab | Purpose |
|---|---|
| Bounces | Main view โ scan accounts, view and filter the bounce list, export CSV |
| Patterns | View and edit the subject rules, IP block patterns, and spam reject patterns |
| Settings | Add, edit, test, and delete IMAP accounts |
| Audit Log | Scan history and processing failure log |
Adding IMAP Accounts
Go to Settings and fill in the form:
| Field | Description | |
|---|---|---|
| Label | Display name, typically the email address | Required |
| IMAP Host | Hostname of the mail server, e.g. mail.yourdomain.com | Required |
| Port | 143 for plain/STARTTLS, 993 for SSL | Required |
| Username | Usually the full email address | Required |
| Password | IMAP password for the account | Required |
| Mailbox | Folder to scan, defaults to INBOX | Optional |
| Use SSL | Check for port 993 (SSL/TLS). Leave unchecked for port 143 | Optional |
| Active | Uncheck to temporarily disable without deleting | Optional |
Click Test after saving to verify the connection. A successful test shows the message count in the mailbox.
Common IMAP settings
| Provider | Host | Port | SSL |
|---|---|---|---|
| Your own Postfix/Dovecot | mail.yourdomain.com | 143 or 993 | Depends on server |
| Gmail | imap.gmail.com | 993 | Yes |
| Outlook / Office 365 | outlook.office365.com | 993 | Yes |
| Yahoo | imap.mail.yahoo.com | 993 | Yes |
For Gmail, you must enable IMAP in Gmail settings and use an App Password (not your regular password) if 2FA is enabled.
Scanning
On the Bounces tab:
- Check the accounts you want to scan in the left panel
- Click Scan & Process
- The tool fetches up to 500 messages per account, classifies bounce emails, logs them to the database, and deletes matched messages from the IMAP server
- A summary appears below the button showing counts and a per-message log
Deletion is permanent. Matched bounce emails are expunged from the IMAP server after processing. Non-bounce emails are never touched. Run the Test connection first if you are unsure about an account.
What gets deleted vs. what stays
| Message type | Action |
|---|---|
| Hard bounce | Logged + deleted from IMAP |
| Soft bounce | Logged + deleted from IMAP |
| IP block | Logged + deleted from IMAP |
| Spam reject | Logged + deleted from IMAP |
| Auto-reply / Out of Office | Deleted from IMAP, not logged |
| No pattern match | Left in inbox, untouched |
| Failed to parse | Deleted from IMAP, logged to Failure Log |
Bounce Types
| Type | Meaning | Exclude from sends? |
|---|---|---|
| Hard | Permanent failure โ address doesn't exist, domain invalid, account permanently closed | Yes โ always |
| Soft | Temporary failure โ mailbox full, server busy, quota exceeded. Soft count increments on each bounce | After repeated bounces |
| IP Block | Your sending IP was blocklisted. The recipient address is valid โ the rejection is about your IP, not them | No โ address is valid |
| Spam Reject | Message rejected as spam by the receiving server. Address is valid, content or reputation is the issue | No โ address is valid |
How addresses are detected
The tool uses a priority chain to extract the bounced email address from each message:
- RFC 3464 Final-Recipient header โ the most reliable method, used by all standard DSN-compliant mail servers
- Original-Recipient header โ fallback DSN header
- VERP pattern โ decodes
bounce+user=domain.com@sender.comReturn-Path addresses - Postfix body pattern โ matches
<email>:in the Postfix NDR body - X-Failed-Recipients header
- To: header of the original message
- First non-system email address in the body โ last resort
If no address can be extracted, the message is logged to the Failure Log and deleted from IMAP.
Patterns
The Patterns tab shows all rules used to detect and classify bounce messages. There are three categories:
| Category | Where it matches | Default count |
|---|---|---|
| Subject rules | Email subject line | 23 |
| IP Block | Email body text | 15 |
| Spam Reject | Email body text | 13 |
Match types
| Type | Behaviour |
|---|---|
starts_with | Pattern must appear at the start of the subject (case-insensitive) |
contains | Pattern can appear anywhere in the subject or body |
regex | Full PHP regular expression, e.g. /^Delivery (Failure|Error)/i |
Result types for subject rules
| Result | Meaning |
|---|---|
hard | Classify as hard bounce immediately |
soft | Classify as soft bounce immediately |
check | Inspect the message body to determine hard/soft/ip_block/spam_reject |
skip | Delete from IMAP without logging (auto-replies, OOO messages) |
Adding a custom pattern
Click Add Pattern, choose category and match type, enter your pattern text, and set the result type. Use Sort Order to control which rules are checked first โ lower numbers run first.
Exporting the Bounce List
The export buttons at the bottom of the Bounces tab produce a CSV file with these columns:
email, bounce_type, soft_count, reason, source_account, bounce_date
| Export | Contents | Use for |
|---|---|---|
| Exclude list (hard+soft) | All hard and soft bounces | Your main suppress list for sending |
| Hard | Hard bounces only | Permanent removes |
| Soft | Soft bounces only | Review and retry decisions |
| All | Every entry including IP blocks and spam rejects | Full audit export |
Using the exclude list in PHP
// Load the exclude list from CSV into an array $exclude = []; if (($fh = fopen('bounces_exclude_2026-06-15.csv', 'r')) !== false) { fgetcsv($fh); // skip header while (($row = fgetcsv($fh)) !== false) { $exclude[] = strtolower(trim($row[0])); } fclose($fh); } // Filter your subscriber list before sending $to_send = array_filter($subscribers, function($sub) use ($exclude) { return !in_array(strtolower($sub['email']), $exclude); });
Using the exclude list with a SQL JOIN
If you imported the CSV into a bounces table on the same server, you can exclude at query time:
SELECT s.* FROM signups s WHERE s.hard_bounce = 0 AND s.email NOT IN ( SELECT email FROM bounce_handler.bounces WHERE bounce_type IN ('hard', 'soft') );
Database Updater Tool
The included update_hard_bounces.php is a standalone web tool that reads an exported hard bounce CSV and sets hard_bounce = 1 in your subscriber table.
Configure the script
Open update_hard_bounces.php and set the DB credentials and table name at the top:
define('DB_HOST', 'localhost'); define('DB_NAME', 'your_database'); define('DB_USER', 'your_user'); define('DB_PASS', 'your_password'); define('SIGNUPS_TABLE', 'signups');
Export from the Bounce Manager
In the Bounce Manager, click Export CSV โ Hard to download the hard bounce list.
Upload the CSV
Open update_hard_bounces.php in your browser, drag and drop (or click to select) the CSV file, and click Upload & Update.
Review results
The tool shows each email address with a โ Updated or โ Not found result, plus totals. Addresses not found in your subscriber table are ignored.
Customising for your schema
If your subscriber table uses different column names, edit these two lines in the script:
// Change 'email' to match your email column name $email_col = array_search('email', array_map('strtolower', $header)); // Change 'hard_bounce = 1' to match your flag column $stmt = $pdo->prepare("UPDATE " . SIGNUPS_TABLE . " SET hard_bounce = 1 WHERE email = ?");
Audit Log
The Audit Log tab shows two tables:
Scan History
Every scan run is recorded with:
- Date and time
- Account scanned
- Emails scanned, bounces found, IP/spam blocks, failures
- Messages deleted from IMAP
- Duration in seconds
Processing Failures
Messages that matched a bounce subject rule but could not be fully parsed are logged here with the subject line, a body snippet, and the failure reason. Review these occasionally to check if you need to add a new pattern for an unusual bounce format.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| Cannot connect to account | Wrong host, port, or credentials | Use the Test button. Check that IMAP is enabled on the mail server and the port is reachable from your web server |
| Unable to negotiate TLS | Server doesn't support STARTTLS on port 143 | Uncheck the SSL option and use port 143, or switch to port 993 with SSL checked |
| SECURITY PROBLEM: AUTH=PLAIN notice in logs | PHP IMAP library warning on plain-text auth | Harmless โ connection still works. Use SSL (port 993) to eliminate the warning |
| 0 bounces found after scanning | Subject patterns don't match your bounce format | Check the Failure Log for clues. Go to Patterns and add a rule matching your bounce subjects |
| โ ๏ธ No recipient found | Body parsing couldn't extract an email address | Check the Failure Log snippet. The message may be in an unusual format โ email a sample to support |
| 413 Request Entity Too Large | Upload limit too low for CSV file | Add client_max_body_size 20M to Nginx, or upload_max_filesize = 20M to php.ini |
| Incorrect string value (MariaDB) | Non-UTF8 characters in bounce reason | Fixed in v2.0 โ the tool sanitizes all text before inserting |
| You have an error in your SQL syntax (ssl column) | Old version used ssl which is a reserved word in MariaDB |
Fixed in v2.0 โ column renamed to use_ssl |
Security Recommendations
- Always use HTTPS. The tool handles IMAP passwords โ never run it over plain HTTP.
- Always use HTTP basic auth or equivalent access control. The tool has no built-in login system.
- Restrict by IP if possible โ in Nginx:
allow 1.2.3.4; deny all;inside the location block. - Do not put it in a public directory without access control. The scan action deletes emails from your server.
- Use a dedicated IMAP account for bounces where possible โ not your main inbox.
- Rotate the database password periodically and update the constant in
imapbm.php.
Changelog
v2.0
- Patterns moved to database โ fully editable in UI without touching code
- VERP bounce address detection added
- Audit log with per-scan statistics and duration
- Failure log for unparseable messages
- Spam Reject bounce type added (separate from IP Block)
- Body extraction improved โ walks full MIME tree including delivery-status parts
- set_time_limit(0) prevents timeouts on large inboxes
- Fixed:
sslreserved word in MariaDB (renamed touse_ssl) - Fixed: non-UTF8 characters in bounce reason causing MariaDB errors
- Fixed: PHP 8.1+ IMAP\Connection type hint
v1.x
- Initial release with hard/soft/ip_block classification
- Multi-account IMAP scanning
- CSV export and DB updater tool
- Account management UI in Settings tab
CloudWizard IMAP Bounce Manager ยท cloudwizard.eu ยท hello@cloudwizard.eu