For developers · v2.7 · GPLv2
Everything below is verifiable in the source. This page exists so the technical detail does not have to be inferred from a feature list, and so you can decide in five minutes whether this belongs in your stack.
Integration surface
The sending path and the logging path are separate. Logging is a passive observer ofwp_mail(), so it keeps working whatever transport you end up using, including none of ours.
| Hook | Priority | What happens there |
|---|---|---|
| wp_mail_from wp_mail_from_name | PHP_INT_MAX | Sender identity, applied last so a theme or another plugin cannot silently win the argument. |
| phpmailer_init | PHP_INT_MAX | Transport configuration: host, port, encryption, auth, Return-Path. |
| pre_wp_mail | 10 | Short-circuits the whole PHPMailer path when the SES API mailer is selected, and builds raw MIME instead. |
| wp_mail | PHP_INT_MAX | Captures recipient, subject, headers and attachment count in memory. Nothing is written yet. |
| wp_mail_succeeded wp_mail_failed | default, then 20 | The logger writes the row, then the monitor updates its counters. One write, after the outcome is known. |
| custom_smtp_purge_logs | daily cron | A single prepared DELETE for everything older than your retention window. |
| custom_smtp_alert_check | 15 min cron | Volume thresholds only, and only when alerts are switched on. Failure streaks are counted inline instead. |
| after_password_reset notification filters | plugins_loaded | Optional switches for WordPress's own admin notification emails: password change, new user, auto-update results, comments. Nothing is off by default. |
| custom_smtp_skip_log | filter | Yours. Return true to exclude the current send from the log and from the alert counters. |
What it costs
Worth stating precisely, because “email logging” is the kind of feature that quietly turns into a performance incident two years later.
date_sent, status, and a 100 character prefix index on recipient. Those are the three columns the log screen actually filters on.Settings in wp_options as a single array, logs in one custom table, alert counters in options, CSV exports streamed to the browser rather than written to disk. Nothing is written to the plugin directory at runtime, so a read-only filesystem andDISALLOW_FILE_MODS are both fine.
Amazon SES · API · IAM role
On ECS Fargate, EC2 or App Runner, Custom SMTP sends through the SESv2 API using the task or instance role. Nothing is pasted into wp-admin, nothing is written to the database, nothing to rotate.
pre_wp_mail and submits raw MIME, so HTML, attachments, CC, BCC, Reply-To and custom headers all survive. BCC stays hidden.Container environment
# mailer selected network wide, no per-site setup CUSTOM_SMTP_MAILER_TYPE=ses_api SES_REGION=eu-west-3 SES_FROM_EMAIL=no-reply@example.com SES_CONFIG_SET=transactional # any setting can be forced the same way CUSTOM_SMTP_LOG_RETENTION=90 CUSTOM_SMTP_STORE_CONTENT=0
Anything supplied by the environment appears read-only in wp-admin with a badge naming the variable it came from, so an administrator can see what the platform controls and stops filing tickets about fields that will not save. The same values can be defined as PHP constants in wp-config.phpif you would rather keep configuration in code than in the environment.
Multisite
Configure the network once and every site inherits it. A site can override what you let it override, and cannot touch what the environment has already pinned.
Precedence runs in one direction and one direction only: environment variables and PHP constants win, then network settings, then the individual site’s own options. That ordering is what makes a fleet predictable, because a client administrator clicking around their own dashboard cannot undo a platform decision.
The log stays per site, which is almost always what you want: the person looking at it is looking for one message, on one site, sent to one person.
Security posture
A plugin that holds SMTP credentials and a copy of your outbound mail should be explicit about how it handles both.
esc_like() and bound, never concatenated.Security reports go through the plugin’s page on WordPress.org rather than a public forum thread. The repository listing carries the current contact and the full changelog, version by version.
Extending it
High-volume transactional traffic you do not want in the log, or a message class you are not allowed to retain, both come down to the same hook.
Excluding a send from the log and the counters
// Keep queue traffic out of the log entirely. add_filter( 'custom_smtp_skip_log', function ( $skip ) { if ( wp_doing_cron() && defined( 'MY_BULK_RUN' ) ) { return true; } return $skip; } );
The plugin registers two abilities for the WordPress Abilities API, so an agent or an orchestration tool can check the mail path without screen-scraping wp-admin:custom-smtp/connection-health andcustom-smtp/send-test-email. Both respect the same capability checks as the admin screens.
Text domain custom-smtp, French shipped in full, including the translated SMTP error messages, which is the part that usually stays in English and matters most to the person reading it.
Compatibility
Tested against current WordPress, and written against the PHP version your oldest client is still on.
| Requirement | Supported | Notes |
|---|---|---|
| WordPress | 6.0 to 7.0 | Uses the WP 6.9 Envelope-From and Return-Path handling when it is available. |
| PHP | 7.4 and up | Typed properties and return types, no PHP 8 only syntax. |
| PHPMailer | 7.0.2 | Configured on phpmailer_init, never replaced or vendored. |
| Multisite | yes | Network settings with per-site overrides. |
| Read-only filesystem | yes | Nothing written outside the database. Streamed exports. |
| Object cache | yes | Settings are read once per request and held in memory. |
| OpenSSL | required | Only for encrypting stored passwords at rest. |
The overview page covers what the plugin does without any of this, which is the page to send a client to when they ask what you just installed on their site.
It is GPLv2 on the WordPress.org repository, so the code is one download away and there is no account between you and it.