Amazon SES
Amazon SES (Simple Email Service) is a low-cost sending service. The site talks to it through the SES version 2 API, so it works on Node, Docker and Cloudflare Workers alike. Use this page if you chose EMAIL_PROVIDER=ses in Email: what the site sends and how to choose a provider.
Settings
| Variable | Required | What it does |
|---|---|---|
EMAIL_PROVIDER | yes | Set to ses. |
EMAIL_FROM | yes | The sender, as Harmony Community Choir <noreply@example.org>. Its domain must be verified in SES. |
AWS_REGION | yes | The SES region, for example us-east-1 or ca-central-1. The site calls https://email.<region>.amazonaws.com. |
AWS_ACCESS_KEY_ID | yes | The IAM user's access key id. |
AWS_SECRET_ACCESS_KEY | yes | The IAM user's secret. Keep it out of files you share; see Keep secrets in files. |
EMAIL_BOUNCE_ADDRESS | no | If set, it is sent to SES as the address that bounce and complaint notices are forwarded to. Empty by default. |
EMAIL_REPLY_TO | no | Where replies go. |
If any of the region, key id or secret is missing, the server refuses to start with:
EMAIL_PROVIDER=ses needs AWS_REGION, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
Set up SES first
These steps happen in your AWS account, not in the site.
-
Verify your domain in the same region as
AWS_REGION. SES gives you DNS records to add (DKIM and, if you like, SPF and a custom mail-from domain). A domain verified in one region is not verified in another. -
Ask for production access. A new SES account is in a sandbox: it can send only to addresses you have verified yourself, so members would not receive their login links. Request production access from the SES console and wait for the answer.
-
Create an IAM user for the site that is allowed to do one thing,
ses:SendEmail, and create an access key for it. For example:{"Version": "2012-10-17","Statement": [{ "Effect": "Allow", "Action": "ses:SendEmail", "Resource": "*" }]}You can narrow
Resourceto your verified identity if you prefer.
A working example
EMAIL_PROVIDER=ses
EMAIL_FROM="Harmony Community Choir <noreply@example.org>"
EMAIL_REPLY_TO=board@example.org
AWS_REGION=ca-central-1
AWS_ACCESS_KEY_ID=AKIAEXAMPLEEXAMPLE
AWS_SECRET_ACCESS_KEY=replace-with-the-secret
# EMAIL_BOUNCE_ADDRESS=bounces@example.org
On Cloudflare, put EMAIL_PROVIDER, EMAIL_FROM and AWS_REGION in wrangler.toml under [vars] and add the two keys as secrets with wrangler secret put AWS_ACCESS_KEY_ID and wrangler secret put AWS_SECRET_ACCESS_KEY.
Slowing down
If SES answers that you are sending too fast (HTTP status 429, or a message saying throttled, too many requests or limit exceeded), the site's client tries again a couple of times with a short pause. If SES is still refusing, that message is counted as not sent. Nothing is written to the server log for this case, so the only sign is on the page: when an announcement hits this, the admin page lists the address as failed. The site does not retry them later: pressing Send again sends to every member again, so lower EMAIL_PER_SECOND first. Lower EMAIL_PER_SECOND to a little under your SES sending rate; see Sender addresses, sending speed and testing.
If something goes wrong
When SES refuses a message for any other reason, the server log says so, with SES's own words:
SES rejected the email: 400 {"message":"Email address is not verified. The following identities failed the check in region CA-CENTRAL-1: ..."}
The usual causes:
- "Email address is not verified": the
EMAIL_FROMdomain is not verified in the region inAWS_REGION, or the account is still in the sandbox and the recipient is not verified. - 403 and a message about the signature or the security token: the access key id or secret is wrong, or the key was deleted.
- 403 and "not authorized": the IAM user lacks
ses:SendEmail.
Then see Troubleshooting: nobody can log in, or emails do not arrive.