Skip to content

Spam and Reputation Filtering

Ra's al Ghul edited this page Aug 9, 2026 · 1 revision

Spam and Reputation Filtering

Processing order

Inbound filtering combines independent signals:

  1. Optional Spamhaus sender-IP and sender-domain reputation.
  2. GTUBE test-string detection.
  3. SpamAssassin score through local spamd or Postmark SpamCheck.
  4. Optional AI classification for questionable scores or filter fallback.
  5. Final spam headers and optional subject tag.

SpamAssassin thresholds

SA_BLOCK_THRESHOLD=12
SA_QUESTIONABLE_THRESHOLD=5
SPAM_SCL_SCORE=9
SPAM_SUBJECT_TAG=[SPAM]
SPAMC_FAIL_OPEN=false
  • Scores at or above the hard-block threshold are spam.
  • Scores at or above the questionable threshold may be sent to the optional AI classifier.
  • Signed real scores are supported, including negative ham scores.
  • GTUBE always produces a spam verdict for testing.

Local spamd mode

SPAMASSASSIN_MODE=local
SPAMD_HOST=127.0.0.1
SPAMD_PORT=783
SPAMD_STARTUP_ATTEMPTS=30
SPAMC_TIMEOUT_MS=10000

The container starts spamd, waits for it to accept connections, and sends the SpamAssassin CHECK protocol request. A valid response uses 0 EX_OK and supplies the signed score.

Postmark SpamCheck mode

SPAMASSASSIN_MODE=postmark
POSTMARK_SPAMCHECK_URL=https://spamcheck.postmarkapp.com/filter

The raw message is submitted with short output enabled. The container does not start local spamd in this mode.

Spamhaus

SPAMHAUS_ENABLED=false
SPAMHAUS_USERNAME=
SPAMHAUS_PASSWORD=
SPAMHAUS_FAIL_OPEN=true

When enabled, Mailbridge authenticates to the Spamhaus Intelligence API, caches tokens and TLD data, and checks public sender IPs and normalized registrable sender domains. Private IPs are skipped. A 401 refreshes the token once. Rate-limit and availability behavior follows SPAMHAUS_FAIL_OPEN.

The sender IP comes from the encrypted Worker envelope, not the Cloudflare HTTP connection.

AI classifier

AI_ENABLED=false
AI_API_KEY=
AI_MODEL=gpt-5.4-nano
AI_BASE_URL=
AI_INPUT_SCOPE=headers
AI_MAX_INPUT_CHARS=20000

Input scopes:

  • headers: message headers only; the default and most privacy-preserving mode
  • attachments: headers plus sanitized attachment filenames, never attachment bodies
  • full_email: includes message body content and should be enabled only when policy permits

The classifier must return structured JSON containing spam, a normalized reason token, and a bounded score. Invalid or unavailable responses are inconclusive.

AI is disabled by default and is secondary screening, not the primary trust boundary.

Headers and subject tagging

Mailbridge injects Exchange-friendly spam status/SCL headers plus a human-facing Mailbridge reason. When the final verdict is spam, it can prepend SPAM_SUBJECT_TAG without duplicating an existing tag.

Failure posture

If SpamAssassin fails, Mailbridge attempts the optional AI fallback. If neither produces a conclusive result, inbound mail is rejected with a temporary 503 by default so Cloudflare can retry. Set SPAMC_FAIL_OPEN=true only after consciously accepting delivery without a working filter.

Clone this wiki locally