Skip to content

SRS — sender rewriting for forwarded mail

Purpose. A forwarder (or a Sieve redirect) re-sends someone else’s message from this server. SPF-strict receivers see the original sender’s domain in the envelope and this server’s IP — an SPF fail, often a DMARC reject. SRS rewrites the envelope sender so the forwarded message passes SPF and bounces still find their way back.

Behaviour.

  • Postfix rewrites the envelope sender of every message whose sender domain is not local through postsrsd (sender_canonical_maps): alice@external.example → SRS0=HHH=TT=external.example=alice@<hostname>. That covers alias forwarders and Sieve redirect. Vacation replies are sent with an empty envelope sender (<>), so there is nothing to rewrite. Mail from local users (:587/:465, local domains) is never rewritten, and From: headers are untouched, so DKIM signatures of the original sender survive.
  • Inbound mail delivered to local mailboxes carries the rewritten address in Return-Path: as well (the standard postsrsd layout); the original sender remains in From: and is recoverable from the SRS address.
  • Bounces to an SRS0=…@<hostname> address are accepted (mydestination = $myhostname), decoded by postsrsd (recipient_canonical_maps) and delivered to the original sender; addresses older than the SRS validity window are rejected.
  • Local domains are excluded from rewriting (srs-exclude, rendered from the active domains with every configuration apply, DKIM rotation, domain create/delete), so a forward between two local domains keeps its sender.
  • The DNS record list gains TXT <hostname> "v=spf1 a -all" (key spf-host, recommended) — the rewritten envelope domain is the hostname, which therefore needs its own SPF record. With an outbound relay configured, this record and the per-domain one both gain the relay’s declared SPF term (DNS records & live verification → Outbound relays): the exclusion above is exactly why the per-domain record needs it — a hosted domain’s envelope sender is never rewritten, so it survives the relay and is what the receiver checks SPF for.

Configuration. SRS_SECRET in .env (generated by mailctl install; mailctl upgrade and restore generate one when it is absent or empty; the compose bundle supplies none, so Postfix refuses to start without it). Changing the secret invalidates in-flight SRS bounce addresses. No admin-facing settings.

Limits & edge cases.

  • postsrsd 1.x reads its excluded-domain list at start, so the watcher restarts it on every apply (docker/postfix/srs.sh); a restart takes well under a second and Postfix retries the TCP map on a transient failure.
  • Forwarding to Gmail/Outlook still counts against this server’s reputation if the forwarded mail is spam; SRS makes forwarding pass, it does not make it wise. Rspamd scores are applied before forwarding.
  • SRS addresses are up to ~90 characters; a few receivers truncate very long local parts.