Tutorial

WordPress sends email through PHP’s mail() function by default, which most hosts either block or deliver straight to spam. Meow Mailer routes that mail through a provider you choose, keeps a log of everything that goes out, and tells you when something fails.

This page walks through the setup, then explains each part of the plugin. It takes about five minutes to get sending.

Quick start

  1. Install Meow Mailer from the WordPress plugin directory and activate it.
  2. Go to Meow Apps → Mailer → Settings. The provider starts as None, which means the plugin stays completely out of the way: WordPress keeps sending the way it did before, and nothing is logged. Activating the plugin never changes how your site sends mail until you choose a provider.
  3. Pick your provider and fill in its credentials.
  4. Set the From address under Sender. Use an address on your own domain.
  5. Send a test email from the Test Email box. It tells you which provider delivered it.

That is the whole setup. Everything below is optional.

Which provider should I choose?

There are two kinds, and the difference matters more than the brand.

Generic SMTP works with any mail server: the one your host gave you, Zoho, Fastmail, Infomaniak, OVH, your own server. If your host handed you a hostname, a username and a password, this is your choice. It is also the option that needs no account anywhere new.

API providers (Mailgun, SendGrid, Brevo, Amazon SES, Postmark, SMTP2GO, Mailjet, Resend, MailerSend, Maileroo) send over HTTPS instead of SMTP. They are usually faster and more reliable at volume, they survive hosts that block SMTP ports, and they give you delivery statistics on their side. They need an account and a verified sending domain.

Gmail / Google Workspace, Microsoft 365 / Outlook and Zoho Mail connect with OAuth 2.0, so no password is ever stored. You click through the provider’s own consent screen.

Microsoft 365 (App-only) authenticates as the app itself with a certificate or a client secret, so nobody signs in at all. It sends from a mailbox you name, and a shared mailbox works without a license. It needs an Entra app registration with the Mail.Send application permission and admin consent.

If you have no preference and no account anywhere: start with Generic SMTP using your host’s server. If email still lands in spam, that is a domain authentication problem (SPF, DKIM, DMARC), and moving to an API provider with a verified domain is the usual fix.

Sender settings

From is the name and address recipients see. It should be on the domain the site runs on, because that is the domain your SPF and DKIM records cover. Sending as [email protected] from your own site is the single most common reason mail goes to spam.

Force From makes your From address win over whatever calls wp_mail(). Leave it off if a plugin legitimately needs its own sender (a contact form using the visitor’s address, for instance). Turn it on when something on your site sends as the wrong address.

Reply-To is where replies go when that should differ from the From address.

Return Path is the envelope sender, meaning where bounce messages are returned. Leave it empty and bounces follow the From address, which is what most sites want. It applies to Generic SMTP and the other PHPMailer based routes; API providers wrap your message in their own envelope and handle bounces on their side.

A fallback provider

Under Email Provider you can set a Fallback Provider: a second route used only when the first one fails on a given email. One extra attempt, immediately, and never more than that.

This is the difference between one provider having a bad afternoon and your site losing its password resets and order confirmations. A sensible pairing is your host’s SMTP as the primary and an API provider as the fallback, or the reverse.

Two SMTP servers at different companies, for example an Outlook relay and your host’s server? Pick Generic SMTP (Second Server) as the fallback. It has the same fields as Generic SMTP and only appears in the Fallback list, never as the main provider.

The fallback can also be WordPress itself, which needs no account at all. Deliverability is whatever your server offers, which is rarely good, but for a password reset, delivered imperfectly beats not delivered.

The log always records which provider actually sent each email, and keeps the failing provider’s error next to it, so a rescued email does not look like a clean send.

The email log

Every email is recorded: date, sender, recipients, subject, the provider that sent it, and the error if there was one. Open an entry to read the message exactly as it was built.

  • Enable Logging turns the log on or off.
  • Store Body also keeps the message itself, so you can read it back and resend it. Turn it off if you would rather not keep email content in your database.
  • Keep Logs For deletes older entries automatically once a day: forever, 7, 30 or 90 days.
  • Resend sends an entry again, through whichever provider is active now. It needs the body, so it is available when Store Body is on.

Keep Attachments

Keep Attachments also keeps the attached files with the log entry, so a resend sends the email complete. This matters when another plugin builds a document on the fly, an invoice PDF being the usual case, because that file cannot be rebuilt later.

It is off by default, since it puts real documents in your database, and it only applies to emails sent after you switch it on. Files are kept for 30 days rather than forever, and an email carrying more than 2 MB of attachments keeps none of them rather than some, so a resend never goes out half complete. Each log entry tells you whether its attachments will travel with a resend.

Failure alerts and the weekly summary

Failure Alerts email you when your site stops being able to send, at most one alert per hour however many emails fail. The alert is sent by WordPress itself rather than through your provider, so it still reaches you when the provider is the problem.

Weekly Summary sends a short email with how much was sent, how much failed, and the most common errors. Weeks where nothing was sent are skipped entirely.

Both can also go to a webhook, so alerts land in Slack, Discord or Teams instead of, or as well as, an inbox.

Multisite: configure the provider once

On a multisite network, the main site’s settings screen has a Multisite section with Shared Settings. Turn it on and the provider is configured once for the whole network: every site sends through it, and subsites cannot change it.

Two groups are optional on top of that, because a network often wants one mail account but different details per site:

  • Sender: the From address, Reply-To and Return Path.
  • Delivery: logging, retention and background sending.

Email logs are never shared. Each site has its own, and sees only its own email.

Keeping credentials out of the database

Any provider credential can be defined as a PHP constant in wp-config.php instead of being stored in the database. The constant always wins over the stored value, which makes it the right choice for a site under version control, for a staging clone that must not inherit production keys, or simply to keep secrets out of database backups.

The pattern is MWMAIL_<PROVIDER>_<FIELD>, in capitals:

define( 'MWMAIL_SMTP_HOST', 'smtp.example.com' );
define( 'MWMAIL_SMTP_PORT', 587 );
define( 'MWMAIL_SMTP_USERNAME', '[email protected]' );
define( 'MWMAIL_SMTP_PASSWORD', 'the-password' );

The full list, by provider:

ProviderConstants
Generic SMTPMWMAIL_SMTP_HOST, MWMAIL_SMTP_PORT, MWMAIL_SMTP_ENCRYPTION, MWMAIL_SMTP_AUTOTLS, MWMAIL_SMTP_AUTH, MWMAIL_SMTP_USERNAME, MWMAIL_SMTP_PASSWORD
Generic SMTP (Second Server, fallback only)MWMAIL_SMTP_SECONDARY_HOST, MWMAIL_SMTP_SECONDARY_PORT, MWMAIL_SMTP_SECONDARY_ENCRYPTION, MWMAIL_SMTP_SECONDARY_AUTOTLS, MWMAIL_SMTP_SECONDARY_AUTH, MWMAIL_SMTP_SECONDARY_USERNAME, MWMAIL_SMTP_SECONDARY_PASSWORD
MailgunMWMAIL_MAILGUN_API_KEY, MWMAIL_MAILGUN_DOMAIN, MWMAIL_MAILGUN_REGION
SendGridMWMAIL_SENDGRID_API_KEY
BrevoMWMAIL_BREVO_API_KEY
Amazon SESMWMAIL_SES_ACCESS_KEY, MWMAIL_SES_SECRET_KEY, MWMAIL_SES_REGION
PostmarkMWMAIL_POSTMARK_SERVER_TOKEN, MWMAIL_POSTMARK_MESSAGE_STREAM
SMTP2GOMWMAIL_SMTP2GO_API_KEY
MailjetMWMAIL_MAILJET_API_KEY, MWMAIL_MAILJET_SECRET_KEY
ResendMWMAIL_RESEND_API_KEY
MailerSendMWMAIL_MAILERSEND_API_KEY
MailerooMWMAIL_MAILEROO_API_KEY
Gmail / Google WorkspaceMWMAIL_GMAIL_CLIENT_ID, MWMAIL_GMAIL_CLIENT_SECRET
Microsoft 365 / OutlookMWMAIL_OUTLOOK_CLIENT_ID, MWMAIL_OUTLOOK_CLIENT_SECRET, MWMAIL_OUTLOOK_TENANT
Microsoft 365 (App-only)MWMAIL_MICROSOFT_TENANT, MWMAIL_MICROSOFT_CLIENT_ID, MWMAIL_MICROSOFT_AUTH, MWMAIL_MICROSOFT_CERTIFICATE, MWMAIL_MICROSOFT_CLIENT_SECRET, MWMAIL_MICROSOFT_MAILBOX
Zoho MailMWMAIL_ZOHO_CLIENT_ID, MWMAIL_ZOHO_CLIENT_SECRET, MWMAIL_ZOHO_DATACENTER

On a multisite network a constant applies to every site, whether or not Shared Settings is on, which is usually exactly what a network admin wants.

For Microsoft 365 (App-only), MWMAIL_MICROSOFT_AUTH is certificate (the default) or secret. MWMAIL_MICROSOFT_CERTIFICATE can also be the path to a PEM file holding the certificate and its unencrypted private key. Keep that file outside the web root and the private key never touches the database at all.

Define only the fields you would type yourself. The OAuth providers also store an access token, a refresh token and an expiry, and Zoho stores its account details. Those rotate on their own and the plugin manages them, so pinning one with a constant would break sending as soon as it expires.

Optional encryption

If you prefer to keep credentials in the database, the Security section has an Enable Encryption button. Every password, API key and OAuth token is then stored encrypted, using the security keys already in your wp-config.php.

It protects against a copy of your database being read: a backup, a dump, a staging clone. It does not protect against someone who can read your files, since the keys live there too.

It is off by default for one reason: if those security keys change, during a migration, a restore onto another site, or a security plugin rotating them, the stored credentials cannot be read any more and email stops until you enter them again. The plugin says so clearly when it happens rather than failing quietly. Export your settings before moving a site, or define MWMAIL_ENCRYPTION_KEY in wp-config.php to pin a key that survives rotation.

Plugins that add their own attachments

Some plugins build part of an email at the last moment, on WordPress’s phpmailer_init hook. Invoice plugins are the common case: they generate a PDF in memory and attach it there, with no file ever written to disk.

Meow Mailer runs that hook the standard way and keeps what those plugins add: attachments held in memory, file attachments, inline images, Cc, Bcc, Reply-To and custom headers. It happens before the provider is chosen, so the same attachment goes out whether your primary provider sends the email or the fallback does, with every provider.

If you ever need to keep that hook out of the way entirely, the mwmail_run_phpmailer_init filter turns it off.

Background sending

Background Send hands the page back to the visitor immediately and does the actual network send once the response has been flushed. It makes checkout and registration feel faster, since nobody waits on your mail provider.

Emails with attachments are never deferred, because the plugin that called wp_mail() is free to delete its temporary file the moment the call returns.

Offline mode: a staging site that cannot email customers

In the provider list there is an Offline entry. Pick it and nothing is ever sent, while every email is still captured in the log with its full content, so you can read exactly what would have gone out.

That is the mode for a clone of a production site. No customer can receive anything from your staging copy while it is on, and you still get to check the content of every email your work triggers.

Filters for developers

FilterWhat it does
mwmail_return_pathThe envelope sender per message, for a dynamic bounce address. Receives the address and the normalized email.
mwmail_run_phpmailer_initReturn false to skip the phpmailer_init compatibility pass for an email.
mwmail_stored_attachment_daysHow long stored attachments live. Default 30.
mwmail_stored_attachment_limitThe most one email may store, in bytes. Default 2 MB.
mwmail_alert_webhookThe webhook payload, before it is sent.
mwmail_allow_setupWho may see and change the settings. Defaults to manage_options.

WordPress’s own wp_mail, wp_mail_from, wp_mail_from_name, wp_mail_content_type, wp_mail_charset, wp_mail_succeeded and wp_mail_failed all keep working exactly as they do without the plugin.

Troubleshooting

The log stays empty. The provider is probably still set to None, which means WordPress is sending by itself and the plugin is not involved. Choose a provider, or note that the log records WordPress’s own sends too once one is chosen.

Another plugin took over sending. If a second SMTP plugin is active, whichever hooks wp_mail first wins and Meow Mailer may never see your email. The Dashboard says so when it detects one. Keep a single mail plugin active.

Email arrives but lands in spam. That is authentication, not the plugin. Your From domain needs an SPF record that includes your provider, and DKIM signing set up on the provider’s side. The Dashboard warns when the From address uses a different domain from the site.

Test email succeeds but real email does not arrive. Check the log: an entry marked Sent with an error next to it means the fallback rescued it, and the error is what your primary provider said.

Credentials stopped working after a migration. If encryption was on and the site’s security keys changed, the stored secrets can no longer be read. Enter them again, or pin a key with MWMAIL_ENCRYPTION_KEY before the next move.

FAQ

Is Meow Mailer free?

Yes, entirely, with no upsell and no tracking.

Does it work on multisite?

Yes. Shared Settings configures the provider once for the whole network, while each site keeps its own log.

Can I keep my SMTP password out of the database?

Yes, define it in wp-config.php as MWMAIL_SMTP_PASSWORD. Every provider credential has an equivalent constant.

Does it store the content of my emails?

Only if Store Body is on, and you can turn it off or set a retention period.

What happens if my provider goes down?

Set a fallback provider and the email is retried through it immediately.

Does it track opens or clicks?

No, never.