{"id":339281,"date":"2026-07-14T16:14:16","date_gmt":"2026-07-14T16:14:16","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/zdn-whitelist-outgoing-email\/"},"modified":"2026-07-14T16:13:52","modified_gmt":"2026-07-14T16:13:52","slug":"zdn-whitelist-outgoing-email","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/zdn-whitelist-outgoing-email\/","author":23212736,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.2","stable_tag":"1.1.2","tested":"7.0.1","requires":"5.0","requires_php":"7.4","requires_plugins":null,"header_name":"ZDN Whitelist Outgoing Email","header_author":"cuongquach.dev","header_description":"Restrict outgoing emails to a whitelist and log every message. Non-whitelisted recipients are replaced with a fallback address (original kept in display name for traceability). Ideal for dev\/staging environments.","assets_banners_color":"0261f1","last_updated":"2026-07-14 16:13:52","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/ko-fi.com\/cuongquach","header_plugin_uri":"https:\/\/cuongquach.dev\/zdn-whitelist-outgoing-email\/","header_author_uri":"https:\/\/cuongquach.dev","rating":0,"author_block_rating":0,"active_installs":0,"downloads":36,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.1.2":{"tag":"1.1.2","author":"cuongquach","date":"2026-07-14 16:13:52"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-256x256.png":{"filename":"icon-256x256.png","revision":3607769,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-772x250.png":{"filename":"banner-772x250.png","revision":3607769,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.1.2"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[267,2236,8537,19979,6147],"plugin_category":[41],"plugin_contributors":[237856],"plugin_business_model":[],"class_list":["post-339281","plugin","type-plugin","status-publish","hentry","plugin_tags-email","plugin_tags-filter","plugin_tags-logger","plugin_tags-staging","plugin_tags-whitelist","plugin_category-communication","plugin_contributors-cuongquach","plugin_committers-cuongquach"],"banners":{"banner":"https:\/\/ps.w.org\/zdn-whitelist-outgoing-email\/assets\/banner-772x250.png?rev=3607769","banner_2x":false,"banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/zdn-whitelist-outgoing-email\/assets\/icon-256x256.png?rev=3607769","icon_2x":"https:\/\/ps.w.org\/zdn-whitelist-outgoing-email\/assets\/icon-256x256.png?rev=3607769","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p><strong>ZDN Whitelist Outgoing Email<\/strong> prevents WordPress from accidentally sending emails to real users in development or staging environments, and keeps a searchable log of everything that left the site.<\/p>\n\n<p>Filtering is <strong>off by default<\/strong> after activation so a live site is not affected until you enable it. When filtering is on and the allowlist is empty, every recipient is redirected to the fallback address.<\/p>\n\n<h4>Features<\/h4>\n\n<ul>\n<li><strong>Whitelist-based filtering<\/strong> \u2014 only allowed email addresses receive emails.<\/li>\n<li><strong>Wildcard domain support<\/strong> \u2014 allow entire domains with <code>*@example.com<\/code>.<\/li>\n<li><strong>Fallback replacement<\/strong> \u2014 non-whitelisted recipients are replaced with your fallback address.<\/li>\n<li><strong>Traceability<\/strong> \u2014 the original email is preserved in the display name (e.g., <code>user@external.com &lt;fallback@site.com&gt;<\/code>).<\/li>\n<li><strong>Outgoing email logger<\/strong> \u2014 capture To, subject, body, headers, attachments, and errors in a custom table.<\/li>\n<li><strong>Filtered badge<\/strong> \u2014 logs show when the whitelist rewrote recipients and what the original address was.<\/li>\n<li><strong>View \/ resend \/ delete<\/strong> \u2014 inspect HTML or raw bodies, resend a message, or clean up bulk rows.<\/li>\n<li><strong>Retention rules<\/strong> \u2014 auto-prune by max row count and\/or age.<\/li>\n<li><strong>Highest priority filter<\/strong> \u2014 hooks at <code>PHP_INT_MIN<\/code> so filtering runs before any other mail plugin; logging runs last at <code>PHP_INT_MAX<\/code>.<\/li>\n<li><strong>CC\/BCC filtering<\/strong> \u2014 also filters CC and BCC headers.<\/li>\n<li><strong>PHP error log option<\/strong> \u2014 optionally log replacements to the PHP error log.<\/li>\n<\/ul>\n\n<h4>Use Cases<\/h4>\n\n<ul>\n<li>Development environments where you don't want emails going to real customers.<\/li>\n<li>Staging sites that mirror production data.<\/li>\n<li>QA testing where only testers should receive emails.<\/li>\n<li>Debugging which plugin sent which email (and whether it was filtered).<\/li>\n<\/ul>\n\n<h3>Privacy<\/h3>\n\n<p>When <strong>email logging<\/strong> is enabled, the plugin stores outgoing message metadata and bodies locally (recipients, subject, body, headers, attachment paths, status, errors, and server address). Access is limited to administrators (<code>manage_options<\/code>).<\/p>\n\n<p>Retention options control automatic deletion by count and\/or age. Individual or bulk logs can be deleted from the Email Logs screen. Uninstalling the plugin removes the log table, plugin options, and related scheduled events.<\/p>\n\n<p>Suggested privacy-policy text is also registered with WordPress under <strong>Settings \u2192 Privacy<\/strong> for site owners to adapt.<\/p>\n\n<p>The optional PHP error-log setting writes replacement events to the server error log only (no external service).<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>zdn-whitelist-outgoing-email<\/code> folder to <code>\/wp-content\/plugins\/<\/code>, or install the release zip via <strong>Plugins \u2192 Add New \u2192 Upload Plugin<\/strong>.<\/li>\n<li>Activate the plugin through the Plugins menu. Filtering starts <strong>disabled<\/strong>.<\/li>\n<li>Go to <strong>Outgoing Email \u2192 Whitelist<\/strong>.<\/li>\n<li>Set your fallback email address.<\/li>\n<li>Optionally add allowed email addresses (one per line). Leave empty to send all outgoing emails to the fallback when filtering is enabled.<\/li>\n<li>Enable filtering when you are ready.<\/li>\n<li>Browse captured mail under <strong>Outgoing Email \u2192 Email Logs<\/strong>.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"will%20this%20break%20mail%20on%20a%20production%20site%20after%20i%20activate%20it%3F\"><h3>Will this break mail on a production site after I activate it?<\/h3><\/dt>\n<dd><p>No. Filtering is off by default. Mail is only rewritten after you enable filtering in settings.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20filtering%20is%20on%20but%20the%20allowlist%20is%20empty%3F\"><h3>What happens if filtering is on but the allowlist is empty?<\/h3><\/dt>\n<dd><p>Every To, Cc, and Bcc recipient is replaced with the fallback address. That is intentional for staging clones of production data.<\/p><\/dd>\n<dt id=\"does%20resending%20a%20log%20send%20a%20real%20email%3F\"><h3>Does resending a log send a real email?<\/h3><\/dt>\n<dd><p>Yes. Resend calls <code>wp_mail<\/code> again. If filtering is still enabled, recipients are filtered again. Only users who can manage options can resend.<\/p><\/dd>\n<dt id=\"where%20are%20email%20logs%20stored%3F\"><h3>Where are email logs stored?<\/h3><\/dt>\n<dd><p>In a custom database table (<code>{prefix}zdnwhoue_email_logs<\/code>) on your site. Nothing is sent to third-party servers by this plugin.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.2<\/h4>\n\n<ul>\n<li>Fix email log detail modal contrast: design tokens now apply on the modal (not only the page wrap) so meta fields and Raw\/HTML message views stay readable.<\/li>\n<\/ul>\n\n<h4>1.1.1<\/h4>\n\n<ul>\n<li>Prefix all public PHP identifiers (classes, functions, options, hooks, script handles) with <code>zdnwhoue<\/code> \/ <code>ZDNWHOUE<\/code> for WordPress.org uniqueness guidelines.<\/li>\n<li>Keep WordPress.org banner\/icon assets out of the distributable plugin zip (SVN assets only).<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Initial release: whitelist-based outgoing email filtering with wildcard domain support (<code>*@example.com<\/code>).<\/li>\n<li>Replace non-whitelisted To\/Cc\/Bcc recipients with a fallback address while preserving the original in the display name.<\/li>\n<li>Hooks at <code>PHP_INT_MIN<\/code> so filtering runs before other mail plugins.<\/li>\n<li>Filtering defaults off after activation for safer installs; empty allowlist redirects all recipients when filtering is on.<\/li>\n<li>Optional PHP error-log entries when a recipient is replaced.<\/li>\n<li>Outgoing email logger (custom table, list table, detail modal with HTML\/raw views).<\/li>\n<li>Track whitelist rewrites on each log row (original recipient + Filtered badge).<\/li>\n<li>Logging settings: enable\/disable, max logs, age-based retention.<\/li>\n<li>Resend, bulk delete, search, and status views (sent \/ failed \/ filtered).<\/li>\n<li>Top-level <strong>Outgoing Email<\/strong> admin menu with branded UI.<\/li>\n<li>Privacy policy helper content and uninstall cleanup of logs\/options.<\/li>\n<\/ul>","raw_excerpt":"Restrict outgoing emails to a whitelist and log every message. Non-whitelisted recipients are replaced with a fallback address. Ideal for dev\/staging.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/339281","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=339281"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/cuongquach"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=339281"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=339281"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=339281"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=339281"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=339281"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=339281"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}