Skip to content

AndreClements/phplist-plugin-bcc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BCC plugin for phpList

A Blind Carbon Copy (BCC) plugin for phpList 3.x that doesn't leak.

Adds BCC recipients to campaign emails. Delivered to the BCC inbox, never visible in the headers other recipients receive. Each campaign carries its own BCC list, with an optional global fallback for the "always BCC this address" case.

Why this exists

phpList 3 has no built-in BCC setting. The community workaround is the generic Custom Header plugin configured with header name Bcc. That delivers a copy to the BCC recipient (correct) and writes a literal Bcc: line into the headers every other recipient can read (the leak).

A real BCC stays out of the headers other recipients see. This plugin uses PHPMailer's addBCC() directly on the mail object, which routes the address correctly for whichever delivery mode phpList is configured to use (per-mode detail in How it works below).

What it does

  • Adds a BCC tab to the campaign editor. Per-campaign BCC addresses live there.
  • Falls back to a global default (Configuration → Settings → BCC addresses) for campaigns that leave the per-campaign field blank.
  • Lets a specific campaign opt out of the global default via a checkbox in the BCC tab.
  • BCC addresses are never visible in the headers other recipients receive.
  • Never BCCs non-campaign mails (subscribe confirmations, password resets, system notifications).

Installation

In phpList admin → Manage Plugins → install from this URL:

https://github.com/AndreClements/phplist-plugin-bcc/archive/main.zip

Then enable the plugin.

Configuration

Per-campaign BCC (primary path)

In the campaign editor, open the BCC tab and enter comma-separated addresses in the textarea:

compliance@example.org, archive@example.org

When the textarea is blank, the help text under it tells you what the campaign will inherit (or that no BCC will be sent).

Global default (optional fallback)

Go to Configuration → Settings and find the Default BCC addresses field under the campaign category. Whatever you put there is applied to every campaign that leaves its own BCC tab blank. Useful for an always-on compliance or tracking inbox.

Opting out per campaign

If a global default is set but a specific campaign needs to send with no BCC at all, open that campaign's BCC tab and tick Ignore global default. The campaign will skip BCC even when the global is configured.

Precedence rules (at send time)

For every campaign send, the plugin evaluates in order:

  1. Non-campaign mail? (no positive messageid on the PHPMailer instance: system mails, subscribe confirmations, password resets) → no BCC.
  2. "Ignore global default" checkbox ticked? → no BCC.
  3. Per-campaign BCC field non-empty? → use those addresses.
  4. Otherwise → use the global default (or no BCC if the global is also blank).

Invalid addresses are silently dropped. If a per-campaign field is filled in but parses to zero valid addresses (typos only, for example), the campaign sends with no BCC and the event is logged to phpList's event log. The plugin does not silently inherit the global default when a per-campaign override was clearly the intent.

How it works

phpList's plugin API exposes a messageHeaders($mail) hook that fires for every outgoing message and passes the PHPMailer instance as $mail. A naive implementation returns array('Bcc' => 'address') from this hook, which phpList writes into the message headers (the leak).

This plugin uses the same hook differently. It mutates $mail directly:

$mail->addBCC($addr);
return array();

addBCC() lets PHPMailer route the BCC correctly for each delivery mode phpList supports:

phpList delivery mode What PHPMailer does with the BCC
smtp (Simple Mail Transfer Protocol host configured) Address added to the RCPT TO envelope only. No Bcc: header in the message body.
mail, sendmail, qmail (no SMTP host configured) A Bcc: header is added to the message. The Mail Transfer Agent (MTA) — PHP's mail() reaching sendmail, postfix, or the Windows SMTP relay — strips it before final delivery. That's the standard MTA behaviour for Bcc: headers, and PHPMailer's own source comments call it out.
amazonSes (Amazon Simple Email Service API) Address passed as a separate API field, never embedded in the message.

In every case the end result is the same. The BCC recipient receives the message, and no other recipient sees the address in the headers they receive.

Note on mail() mode. The Bcc: header is technically written, then stripped by the MTA. This is correct behaviour for sendmail, postfix, qmail, and PHP's Windows SMTP relay (all well-behaved). On an obscurely-configured MTA that doesn't strip Bcc:, the leak would reappear. Run the verification step below if you're not sure.

Per-campaign configuration is stored in phpList's standard messagedata key-value table. No custom schema, nothing to migrate, nothing to clean up at uninstall. The campaign-editor tab is added via the standard sendMessageTab / sendMessageTabTitle / sendMessageTabInsertBefore hooks; form fields are auto-persisted by phpList on save.

Verifying the install

Send a campaign test to an inbox you control (Gmail is convenient) with a BCC address set. View the full headers of the received message. Look for a Bcc: line. With this plugin installed, you should see none. The BCC recipient still receives the message, the visible headers stay clean.

Compatibility

  • phpList 3.x. Uses the standard phplistPlugin base class, the messageHeaders hook, and the sendMessageTab* hooks (all present since phpList 3.0).
  • Relies on PHPMailer's addBCC() method, part of PHPMailer since v5.x. phpList 3 has shipped PHPMailer-based sending since its initial 3.0 release.

License

GPL-3.0-or-later. See LICENSE.

Author

Andre Clements. Initial v0.1 written during a bulk campaign send (May 2026) where the BCC leak was discovered in pre-send testing. v0.2 generalised the plugin to per-campaign configuration.

About

phpList 3.x plugin that adds BCC recipients as SMTP envelope-only (no header leak). Drop-in replacement for the Custom Header plugin's BCC use case.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages