# Antispam

> How CorePanel scores incoming mail with Rspamd, and how to tune thresholds and allow/deny lists per server, per domain or per mailbox.

Source: https://www.corepanel.net/docs/email/antispam/
Last updated: 2026-09-14
Part of the CorePanel documentation — https://www.corepanel.net/docs

---

Incoming mail is scored by **Rspamd**. Every message accumulates points from dozens of
checks — reputation, DNS blocklists, SPF/DKIM/DMARC results, content, greylisting — and
the total decides what happens to it.

CorePanel exposes two controls on top of that: **thresholds** and **allow/deny lists**,
each settable at three levels.

## The three thresholds

| Action | Default score | What it does |
|---|---|---|
| **Greylist** | 4 | Temporarily defers the message; a legitimate sender retries and gets through, most spam bots do not |
| **Add header** | 6 | Marks the message as spam and files it in the recipient's **Junk** folder |
| **Reject** | 15 | Refuses the message at SMTP time |

Rejecting at SMTP time is the honest option: the sender gets an error and knows the mail
did not arrive. That is why the reject threshold is high — a false positive here means
losing a real message with no copy anywhere.

Greylisting defers a sender the server has never seen for **5 minutes**; its retry after
that is accepted, and the sender is remembered for a day. The delay applies once per new
sender, not to every message — but it is a real delay, so a domain that sends you
time-critical mail (verification codes, order confirmations) is a good candidate for the
allow list below.

### Choosing thresholds

- **Lower the reject score** (say to 12) only when you are confident about your mail
  flow, and after watching what a lower score would have caught.
- **Lower the add-header score** to mark more aggressively; it is a safe knob, since
  nothing is lost — the message is still delivered.
- **Raise the greylist score** if legitimate senders complain about delays.

Leaving a threshold at zero means "use the default", so a policy only has to carry the
values you actually want to change.

## Where a flagged message lands

A message that reaches the add-header threshold is stamped `X-Spam: Yes` and delivered to
the recipient's **Junk** folder, not the inbox. A message above the reject threshold never
reaches a mailbox at all — it is refused during the SMTP session.

The folder is created and subscribed automatically, so it is there on a mailbox nobody has
opened yet, and it is the same folder in webmail and in any IMAP client.

The `X-Spam` header is safe to filter on yourself: Rspamd strips any `X-Spam` header a
sender supplies before adding its own, so an incoming message cannot forge one.

Moving messages in and out of Junk is also how you teach the filter:

- Drag a message **into** Junk → it is learned as spam.
- Drag a message **out of** Junk → it is learned as ham.

What is learned goes into **one corpus for the whole server**, not one per mailbox. That
is deliberate: the classifier ignores its own verdict until it has learned 200 messages
of each kind, and a per-mailbox corpus on a hosting server never gets near that — split
across the mailboxes of a real server it was single digits each, so the classifier
stayed silent for everyone. Shared, the same traffic crosses the threshold and then helps
every mailbox, new ones included.

The trade is worth stating plainly: one person marking legitimate mail as spam nudges a
classifier everybody shares. It is bounded — 200 messages before it counts for anything,
and it is one signal among dozens rather than a verdict — and it is what every shared
mail server does, because the alternative measured here was a classifier that did nothing
for anyone.

> **Servers upgraded from an earlier version**
>
> Training only started working in **1.6.13**. Before it, the helper the training scripts
> call was not on the path Dovecot searches, so every message ever dragged into Junk taught
> the filter nothing, and the only learning that happened was the server's own automatic
> kind. Nothing needs doing — the fix applies on upgrade — but a server that has just been
> upgraded starts its shared corpus from scratch and takes a few weeks of real mail before
> Bayes contributes.
> **Mailboxes migrated from cPanel**
>
> cPanel filed spam in a folder called `spam`, and a migrated mailbox still has it, with its
> old contents. New spam arrives in **Junk** from now on; the old folder is left untouched so
> nothing is lost, and it can be deleted once its contents have been reviewed.
## Blocklists need a resolver of your own

Most of Rspamd's strongest signals are DNS blocklists — Spamhaus ZEN for sending IPs,
Spamhaus DBL for domains found in the message, dnswl for known-good senders. **These
lists refuse queries that arrive through a shared public resolver**, which is what a
hosting provider's default nameservers are. Instead of a verdict they answer
`127.255.255.254`, meaning "you are querying from a public resolver".

Rspamd reacts to that badly, and silently: its health probe sees an answer where there
should be none, concludes the list is broken, and **stops querying it entirely**. The only
trace is a recurring line in `/var/log/rspamd/rspamd.log`:

```
rspamd_monitored_dns_cb: DNS reply returned 'no error' for zen.spamhaus.org
while 'no records with this name' was expected (likely DNS spoofing or BL internal issues)
```

A server in that state still filters — greylisting, SPF/DKIM/DMARC, content rules and
fuzzy hashes all keep working — but it has lost the checks that catch a brand-new phishing
domain on its first day. In one measured case a phishing message imitating WeTransfer
scored **-0.10** and went straight to the inbox; with the blocklists answering, the same
message scored **13.40** and was filed in Junk. Its domain was listed in Spamhaus DBL as
malware (+7.5) and its sending IP in ZEN (+3.0).

CorePanel installs its own recursive resolver (pdns-recursor, on `127.0.0.1:5301`) and
points Rspamd at it, which is why the lists answer normally. That is configured in
`/etc/rspamd/local.d/options.inc` and applied on install and on every upgrade.

### Checking it on your server

```bash
# Spamhaus' own test entry. 127.0.0.x means the list is answering you;
# 127.255.255.254 means your resolver is blocked.
dig +short @127.0.0.1 -p 5301 2.0.0.127.zen.spamhaus.org

# Any complaint here means at least one list has been switched off
grep rspamd_monitored_dns_cb /var/log/rspamd/rspamd.log | tail
```

If you run your own resolver elsewhere, or your provider gives you a dedicated one, point
Rspamd at it by editing the `nameserver` line in `/etc/rspamd/local.d/options.inc` and
restarting `rspamd`. Deleting the file makes Rspamd fall back to `/etc/resolv.conf`.

## Three levels

| Scope | Applies to |
|---|---|
| **Server** | Every domain hosted here |
| **Domain** | One domain |
| **Mailbox** | One address |

The most specific scope wins: a mailbox policy overrides its domain's, which overrides
the server's. That is what lets one demanding address (`abuse@`, `support@`) be more
permissive than the rest of the server without weakening everything.

## Allow and deny lists

Beyond scores, you can name senders explicitly:

| List | Effect |
|---|---|
| **Allow** | Mail from this sender is never actioned as spam, whatever it scores |
| **Deny** | Mail from this sender is always rejected, whatever it scores |

Entries are full email addresses, and a sender can appear only once per scope — allow and
deny are mutually exclusive there. Lists take precedence over thresholds at every level,
and a more specific scope's list wins over a broader one.

> **Allow lists are forgeable**
>
> An allow entry matches the sender address, and sender addresses are trivial to forge. An
> allow-listed address is a permanent opening for anyone willing to spoof it. Use allow
> entries to fix a specific recurring false positive — a supplier, a monitoring system —
> not as a general "trusted senders" list.
## A practical order of operations

When a customer says "we are getting too much spam":

0. Check the blocklists are answering (see
   [above](#blocklists-need-a-resolver-of-your-own)). A server whose resolver Spamhaus
   refuses has lost its best signal, and no amount of threshold tuning replaces it.
1. Look at what is actually arriving and what it scores. If it is scoring 7–12 it is
   already being filed in Junk, and the complaint is really "I still see it in Junk".
2. **Lower the add-header threshold** on that domain before touching reject.
3. Only then consider a lower reject threshold, and only for that domain.

When they say "a message never arrived":

1. Check whether it was rejected (the sender has a bounce with a reason) or marked.
2. If it was rejected, add an **allow** entry for that sender at the narrowest scope that
   fixes it — mailbox before domain, domain before server.
3. If it keeps happening across many senders, the threshold is too aggressive rather than
   any one sender being wrong.

## Outgoing mail

Antispam settings govern **incoming** mail. Whether your outgoing mail is accepted
elsewhere depends on SPF, DKIM and DMARC — see
[email authentication](https://www.corepanel.net/docs/email/mailboxes#email-authentication) — and on the
reputation of the server's IP address.
