# 24-Apr-2026 — SPF/DMARC management, plain-text alternatives, List-Unsubscribe header

## GitHub [#186](https://github.com/nbarrett/ngx-ramblers/issues/186) — [commit 77db1d9](https://github.com/nbarrett/ngx-ramblers/commit/77db1d9fbe0b9c925c198ddca31ef6ec82806c46)

_____

Three deliverability fixes for transactional email, headlined by a new **Email Authentication** panel on the Brevo Senders page that lets admins fix SPF and DMARC with one click.

## Email Authentication panel

The new panel sits above the existing MX records panel and shows colour-coded badges for SPF and DMARC, the current record contents, and a **Fix Auth Records** button that merges missing includes or creates the records. Existing policies are never widened; multi-record SPF states disable the button with a tooltip pointing at manual consolidation.

### SPF missing the Brevo include

![](https://ngx-ramblers.org.uk/api/aws/s3/site-content/2c0fb82e-b576-4806-bf9e-247fcb7550d1.png)

*ashfordramblers.org.uk — SPF exists for the Cloudflare mail path but misses `spf.brevo.com`. DMARC is already in place and is left untouched.*

### Stricter existing DMARC

![](https://ngx-ramblers.org.uk/api/aws/s3/site-content/ea2b9a7b-7fcf-478e-99b6-2f9d627abc2c.png)

*canterburyramblers.org.uk — same SPF gap, but this domain runs `p=reject`. The fix will merge in the Brevo include while preserving the stricter policy exactly.*

### SPF fixed — strict DMARC preserved

![](https://ngx-ramblers.org.uk/api/aws/s3/site-content/f059120c-d169-4145-a5f4-5efd1eb047fd.png)

*canterburyramblers.org.uk after **Fix Auth Records** — SPF now contains both `_spf.mx.cloudflare.net` and `spf.brevo.com`; `p=reject` is retained.*

### Fresh domain with nothing in place

![](https://ngx-ramblers.org.uk/api/aws/s3/site-content/fb4dffc5-7c30-4d52-b93d-388b59b2524a.png)

*berkshire-weekend-walkers.ngx-ramblers.org.uk — nothing at all. The panel shows exactly what will be created before the button is pressed.*

### After one-click fix on a fresh domain

![](https://ngx-ramblers.org.uk/api/aws/s3/site-content/817a914f-12ec-47a7-a450-702195e80677.png)

*Fresh `v=spf1 include:_spf.mx.cloudflare.net include:spf.brevo.com ~all` created, plus a monitor-only `v=DMARC1; p=none` so the admin can raise enforcement later without surprise.*

## Other fixes shipping with this change

- **Plain-text alternative** — every transactional email now gets a `textContent` derived from HTML when the caller supplied none. HTML-only mail is a documented spam-filter penalty signal.
- **List-Unsubscribe header** — `<mailto:replyTo?subject=unsubscribe>` is added to transactional sends unless the caller sets its own. Campaigns are unchanged; Brevo manages their unsubscribe headers natively.
- **Migration iterates every Brevo-registered domain**, not just the env's configured `baseDomain`. Per-domain failures are logged and skipped — one bad zone can't block the rest.
- **www-vs-apex false warning fixed** — a new shared `apexHost()` helper strips `www.` on both sides of the site/Brevo-domain comparison.
- **Cleaner 500 responses** — a shared `errorResponse(error)` returns `{code, message}` so clients see the real cause instead of a serialised empty Error.
- **Idempotent destination-address creation** — POSTing an existing address returns the match instead of tripping Cloudflare's "verification sent too recently" cooldown.