Cloudflare Email
On this page
Route all WordPress email through the Cloudflare Email Sending API and keep a searchable log of every message. Configured entirely through wp-config.php constants — there is no settings screen.
Tiny surface. No admin settings; the only screen is the log viewer (Tools → Cloudflare Email), built with WordPress DataViews.
Drop-in. Hooks
wp_mail()without redefining it — every plugin/theme that sends mail is routed automatically, with Cc/Bcc/Reply-To/custom headers/inline images preserved.Logged. Every send (and every failure) is recorded; view, resend, or delete from the log.
Self-updating. Pulls new versions straight from GitHub Releases.
Requirements
WordPress 7.0+ (the log viewer relies on the WordPress Design System tokens that core 7.0 ships) and PHP 8.4+.
A Cloudflare account with Email Sending enabled for your sending domain (see Cloudflare's setup guide;
npx wrangler email sending enable yourdomain.com).A Cloudflare API token with the Email Sending permission.
Deliverability records (SPF/DKIM/DMARC) and domain onboarding are managed in Cloudflare — this plugin only sends.
Configuration
Add to wp-config.php:
// Required
define('CLOUDFLARE_EMAIL_ACCOUNT_ID', 'your-cloudflare-account-id');
define('CLOUDFLARE_EMAIL_API_TOKEN', 'token-with-email-sending-permission');
// Recommended — the From domain must be onboarded to Cloudflare Email Sending.
// (WordPress otherwise defaults to wordpress@<site-domain>, which is usually unverified.)
define('CLOUDFLARE_EMAIL_FROM', 'no-reply@your-verified-domain.com');
define('CLOUDFLARE_EMAIL_FROM_NAME', 'Your Site');
// Optional
define('CLOUDFLARE_EMAIL_LOG', false); // stop logging *successful* sends
// (failures are always logged); default true
define('CLOUDFLARE_EMAIL_LOG_RETENTION_DAYS', 30); // auto-prune window; 0 = keep foreverWhen the required constants are absent the plugin does nothing and WordPress mail continues through its normal transport. In production wp-admin shows a notice saying so; outside production (WP_ENVIRONMENT_TYPE other than production) it stays quiet, since that is the expected state — a development shell catches mail in Mailpit, a staging site may have no account.
If Cloudflare rejects a message (e.g. an unverified sender, or a custom header it disallows) the send is logged as failed and wp_mail() returns false — there is no silent fallback. To adjust the outgoing payload, filter it:
add_filter('cloudflare_email_payload', function (array $payload) {
unset($payload['headers']['X-Some-Header']);
return $payload;
});Verifying
With WP-CLI:
wp cloudflare-email verify # check the API token
wp cloudflare-email send-test you@example.comThen open Tools → Cloudflare Email to see the log entry.
Development
The log viewer is a TypeScript DataViews app bundled with rolldown; the dev shell and release build are driven by Nix (with direnv):
direnv allow # or: nix develop
composer install # PHP deps (plugin-update-checker)
npm install # JS deps (rolldown, typescript, oxlint, @wordpress/dataviews)
npm run dev # watch-build the DataViews app (rolldown --watch)
npm run build # production bundle -> build/
npm run check # oxfmt + oxlint + type-aware oxlint + tsc (the CI gate)npm run build emits build/index.js (IIFE), build/index.asset.php (the WordPress
dependency manifest), and build/index.css (the DataViews stylesheet). @wordpress/dataviews and its non-core deps are bundled; every other @wordpress/* package resolves to its core wp.* global at runtime.
Linting/formatting use the OXC toolchain — oxlint (all categories
at error, plus a type-aware pass via oxlint-tsgolint) and oxfmt. npm run check runs the full gate; npm run lint:fix / npm run format apply autofixes. The same
gate runs in CI on every push and pull request.
Build the distributable, self-contained zip (bundles vendor/ and the compiled build/):
nix build .#zip -L # -> result/cloudflare-email.zipReleasing
Commits to main follow Conventional Commits. Release Please maintains a release PR; merging it bumps the version in composer.json, package.json, and the plugin header, updates CHANGELOG.md, tags vX.Y.Z, and the release workflow builds cloudflare-email.zip with Nix and attaches it to the GitHub Release. Client sites self-update from that asset via plugin-update-checker.
Part of the Cloudflare Email suite
Four open-source projects work together to give business systems email without SMTP credentials or IMAP polling:
| Project | Role |
|---|---|
| cloudflare-email-relay | Multi-tenant inbound Worker: stores each message in R2, then delivers it to the right system with signed, retried requests |
| cloudflare_email_delivery | Frappe and ERPNext adapter |
| mail_cloudflare | Odoo adapter |
| wordpress-cloudflare-email | WordPress plugin: outbound mail and a delivery log |
Need it set up for your business? Avunu can help.
License
MIT © Avunu LLC