PHPackages                             mantekio/wp-ses-mail - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Mail &amp; Notifications](/categories/mail)
4. /
5. mantekio/wp-ses-mail

ActiveWordpress-muplugin[Mail &amp; Notifications](/categories/mail)

mantekio/wp-ses-mail
====================

WordPress must-use plugin that sends mail through Amazon SES with an IAM role, so no SMTP credentials are stored and no send failures pass silently.

v0.9.0(1mo ago)20GPL-2.0-or-laterPHPPHP &gt;=7.4

Since Jul 15Pushed 1mo agoCompare

[ Source](https://github.com/mantekio/wp-ses-mail)[ Packagist](https://packagist.org/packages/mantekio/wp-ses-mail)[ Docs](https://github.com/mantekio/wp-ses-mail)[ RSS](/packages/mantekio-wp-ses-mail/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (1)Versions (2)Used By (0)

WP SES Mail
===========

[](#wp-ses-mail)

[![Packagist Version](https://camo.githubusercontent.com/06fadd285d03e70628d3b26b09af17323379767bf2e96b347b89e3c640b7678e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d616e74656b696f2f77702d7365732d6d61696c)](https://packagist.org/packages/mantekio/wp-ses-mail) [![License: GPL v2](https://camo.githubusercontent.com/34fd4798bfe47593a94283d1d4fb5c522738d07b84c13f374f10485c0e0ffbbf/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d47504c76322d626c75652e737667)](LICENSE)

A WordPress **must-use plugin** that sends mail through **Amazon SES**, authenticated by an **IAM role**, so there are no credentials stored anywhere and no send failures that pass silently.

> ⚠️ **Status: `0.9.0` (pre-1.0).** Feature-complete and hardened through repeated adversarial review, with the SES signing checked against AWS's own SigV4 test vectors. It has **not yet been run end-to-end against a live Amazon SES account**, so treat it as pre-production until `1.0.0`. It ships in the open while that verification runs; issues and field reports are welcome.

> 📖 **Full write-up:** [Your WordPress mail credentials are sitting in your database](https://www.mantek.io/insights/wordpress-email-ses)

The problem
-----------

[](#the-problem)

By default `wp_mail()` hands your message to PHP's `mail()` and out through `sendmail`: no sending reputation, no SPF alignment, no DKIM signature, so modern providers bin it as spam, often without even a bounce. And `wp_mail()`'s entire failure vocabulary is a single `false`. On the default path it does not even return that: `sendmail` accepts the message, `wp_mail()` returns `true`, and the mail dies downstream where WordPress never hears about it. A password reset that never arrives, with nothing in the logs.

The usual fix is an SMTP plugin. It fixes authentication, but it stores the SMTP username and password in `wp_options`. Every database dump, backup, and staging clone then carries a credential that can send mail as your domain, DKIM-signed, with no fingerprint that it was not you. That is a liability with a settings screen.

What it does
------------

[](#what-it-does)

- **Sends via the SES API, not SMTP.** A signed HTTPS request, so there is no long-lived password in the flow at all.
- **Authenticates with an IAM role.** Credentials come from the AWS default chain (env vars, then the ECS task role, then the EC2 instance role), read at send time and never written to the database, `wp-config.php`, or disk. There is no secret at rest to leak.
- **Replaces only the transport.** It hands core a **subclass** of PHPMailer on `pre_wp_mail` and overrides one method, `postSend()`. Core still builds the whole message and honours every `wp_mail_*` filter, so no core contract is reimplemented and nothing can drift.
- **Fails loudly, never falls back.** An SES rejection returns `false` with the real SES error via `wp_mail_failed`. A misconfiguration **refuses to send** (on every admin screen and in the log) rather than falling through to `sendmail` and spam.
- **Remembers bounces and complaints.** A signed, topic-pinned SNS endpoint records hard bounces and complaints to a suppression table that is checked before every send, so a dead or complaining address is never mailed again and your SES reputation stays intact.
- **Ships a real health check.** `wp ses verify` signs live requests with the same code the send path uses, and **exits non-zero** when anything is wrong, so `wp ses verify || alert` works in cron.

Requirements
------------

[](#requirements)

- **WordPress 5.7+**, for the `pre_wp_mail` hook. On older versions there is no seam: mail falls through to `sendmail` and the plugin can warn but cannot stop it.
- **PHP 7.4+**, with the `openssl` and `curl` extensions.
- WordPress running on **AWS compute with an IAM role** allowing `ses:SendEmail` (plus `ses:GetAccount` and `ses:GetEmailIdentity` for `verify`). Off AWS, the standard `AWS_*` environment variables work.
- A **verified SES identity** to send from (a domain, with Easy DKIM, is what makes mail align).

Installation
------------

[](#installation)

A must-use plugin loads before regular plugins and cannot be deactivated by accident.

**Manual**

```
mkdir -p wp-content/mu-plugins
cp wp-ses-mail.php wp-content/mu-plugins/
```

**Composer**

```
composer require mantekio/wp-ses-mail
```

Composer installs it as a [`wordpress-muplugin`](https://github.com/composer/installers), which has two consequences worth knowing:

- **Allow the installer plugin.** On a fresh project Composer blocks `composer/installers` until you permit it. Add this to your root `composer.json` (most WordPress-Composer stacks already have it): ```
    { "config": { "allow-plugins": { "composer/installers": true } } }
    ```
- **Make sure it actually loads.** It installs into `wp-content/mu-plugins/wp-ses-mail/` (a subfolder), and vanilla WordPress only auto-loads `*.php` placed *directly* in `mu-plugins/`. Rely on a mu-plugins autoloader (Bedrock and similar stacks ship one), or use the **Manual** method above to drop the single file straight in.

Configuration
-------------

[](#configuration)

Define in `wp-config.php` (never the database):

ConstantRequiredMeaning`WP_SES_REGION`yes\*SES region, e.g. `eu-west-1`. \*Falls back to the `AWS_REGION` environment variable.`WP_SES_FROM`yesDefault From address, a verified SES identity, e.g. `noreply@example.com`.`WP_SES_SNS_TOPIC_ARN`for bouncesThe SNS topic ARN SES publishes bounce/complaint events to. The endpoint fails closed until this is set (see [Bounces](#bounces-and-complaints)).`WP_SES_CONFIGURATION_SET`noSES configuration set, for event tracking.`WP_SES_ALERT_EMAIL`noBest-effort address to alert on a serious send failure.`WP_SES_ENDPOINT`test onlyOverride the SES endpoint at a local simulator (e.g. LocalStack). Leave unset in production.```
define( 'WP_SES_REGION', 'eu-west-1' );
define( 'WP_SES_FROM',   'noreply@example.com' );
define( 'WP_SES_SNS_TOPIC_ARN', 'arn:aws:sns:eu-west-1:123456789012:ses-events' );
```

No credentials go here. The IAM role provides them.

Bounces and complaints
----------------------

[](#bounces-and-complaints)

SES holds you to bounce and complaint thresholds; cross them and it throttles your sending, then suspends the account. WordPress has no memory of a bounce and will email a dead address forever, so the plugin keeps that memory for it.

1. In SES, set the identity's bounce and complaint notifications to publish to an **SNS topic**, and set `WP_SES_SNS_TOPIC_ARN` to its ARN.
2. Subscribe the topic to the endpoint `https://your-site/wp-json/wpses/v1/sns`. The plugin auto-confirms the subscription.
3. From then on, hard bounces and complaints are written to the suppression table and blocked before the next send.

**Security.** That endpoint is a public URL. AWS shares one regional signing certificate across all customers, so a valid signature alone only proves a message came from *some* topic. The endpoint therefore **pins your topic ARN** and **verifies the SNS signature** on every message, and **fails closed** until the ARN is set, so no one can forge a notification to suppress your users' mail.

Lift a wrongly-suppressed address:

```
wp ses unsuppress --email=someone@example.com
```

Verifying
---------

[](#verifying)

```
wp ses verify
```

It reports region, credential source, identity verification, DKIM, sandbox state, quota, and whether the bounce topic is wired up, and it **exits non-zero** on any problem, so it composes into cron:

```
# nightly: page if the SES config ever drifts
0 3 * * *  cd /var/www/site && wp ses verify || wp ses verify | mail -s "SES broken on $(hostname)" ops@example.com
```

Hooks
-----

[](#hooks)

Actions you can hook for logging, metrics, or alerting:

ActionFires when`wpses_sent`A send succeeded. Passes the SES `MessageId` and the `Destination`.`wpses_send_failed`A send failed. Passes a `WP_Error`.`wpses_suppression_unreadable`The suppression table could not be read, so the plugin failed open loudly (sent anyway) rather than block mail on a database blip.Known limits
------------

[](#known-limits)

- It is a **transactional transport, not a bulk queue.** A newsletter blast to tens of thousands of subscribers wants a queue and probably a dedicated bulk sender; leaning on this for that just meets SES throttling as a failed send.
- **No retry or backoff yet.** A refused send fails loudly and stops; durable retries belong in a queue in front of the plugin.
- **Message-size ceiling.** SES caps the size of a single message; a mail with very large inline attachments will fail rather than be silently truncated.
- **Alignment is DNS, not the plugin.** DKIM, SPF, and DMARC alignment are records you add. The plugin signs the request and can tell you they are missing, but cannot add them for you.
- **WordPress 5.7+ only** (see [Requirements](#requirements)).

How it works
------------

[](#how-it-works)

The full reasoning (why a subclass and not the `pre_wp_mail` fork, why loud failure and no fallback, the SNS signature trap, and the newsroom bounce loop) is in the write-up:

**→ [Your WordPress mail credentials are sitting in your database](https://www.mantek.io/insights/wordpress-email-ses)**

License
-------

[](#license)

[GPL-2.0-or-later](LICENSE): same as WordPress.

---

Built and maintained by **[ManTek Technologies](https://www.mantek.io)**: WordPress + AWS at scale, for newsrooms and beyond.

###  Health Score

31

—

LowBetter than 65% of packages

Maintenance90

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity23

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

46d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/216154993?v=4)[ManTek Technologies](/maintainers/mantekio)[@mantekio](https://github.com/mantekio)

---

Top Contributors

[![jaafarabazid](https://avatars.githubusercontent.com/u/11146126?v=4)](https://github.com/jaafarabazid "jaafarabazid (2 commits)")

---

Tags

amazon-sesawsemailiammupluginphptransactional-emailwordpresswordpress-pluginwp-mailwordpressawsemailsmtpsesiammupluginphpmailerwp-mail

### Embed Badge

![Health badge](/badges/mantekio-wp-ses-mail/health.svg)

```
[![Health](https://phpackages.com/badges/mantekio-wp-ses-mail/health.svg)](https://phpackages.com/packages/mantekio-wp-ses-mail)
```

###  Alternatives

[helsingborg-stad/municipio

A bootstrap theme for creating municipality sites.

4028.8k10](/packages/helsingborg-stad-municipio)[itinerisltd/wp-phpmailer

WP PHPMailer provides a clean and simple way to configure WordPress-bundled PHPMailer library, allowing you to quickly get started sending mail through a local or cloud based service of your choice.

6331.9k](/packages/itinerisltd-wp-phpmailer)[putyourlightson/craft-amazon-ses

Amazon SES mailer adapter.

1194.8k2](/packages/putyourlightson-craft-amazon-ses)[juhasev/laravel-ses

Allows you to track opens, deliveries, bounces, complaints and clicked links when sending emails through Laravel and Amazon SES

1810.3k](/packages/juhasev-laravel-ses)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
