cPanel email goes to spam for four main reasons: the message fails SPF, DKIM or DMARC; the server's reverse DNS does not match the name it announces; the server IP is on a blocklist; or one account on the server is sending spam and has damaged the IP's reputation. Check them in that order, starting with the headers of a single test message.
Last reviewed 10 October 2026. Disclosure: I sell server and mail troubleshooting as a service, so I am not a neutral source. The commands need root SSH access to a cPanel and WHM server, where Exim handles mail. On a reseller plan you can run the header, DNS and blocklist checks and must ask your provider for the rest.
What does each symptom usually point to?
Each symptom narrows the cause, so match yours first.
| Symptom | Likely cause | First check |
|---|---|---|
| One domain's mail lands in spam; others on the server are fine | That domain fails SPF, DKIM or DMARC | The Authentication-Results header of a test message |
| Every domain's mail lands in spam | IP reputation: no PTR record, a blocklist entry or a spam source on the server | dig -x on the IP, a blocklist lookup, then exim -bpc |
| Bounces say the IP is blocked or listed | A public blocklist, or the receiving provider's own block | The full bounce text, which usually names the list |
| Website form mail lands in spam; mailbox mail is fine | The script's sender address does not match the From domain | smtp.mailfrom against header.from in a form message |
| Mail is slow and the queue holds thousands of messages | A hacked mailbox or script sending in bulk | exim -bp | exiqsumm |
How do you confirm the problem with one test message?
Send one plain message from an affected mailbox to a mailbox you control at a large provider, open the raw message and read the Authentication-Results header. A healthy result at Gmail looks like this:
Authentication-Results: mx.google.com;
dkim=pass header.i=@example.com header.s=default header.b=AbCdEfGh;
spf=pass (google.com: domain of you@example.com designates 203.0.113.10 as permitted sender) smtp.mailfrom=you@example.com;
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=example.com
spf tests the envelope sender (smtp.mailfrom) against the sending IP. dkim tests the message signature against a public key in DNS. dmarc passes only when SPF or DKIM passes for the same domain as the visible From address (header.from). Trouble reads as spf=softfail, dkim=none, dkim=fail or dmarc=fail.
One cPanel trap: mail sent by a website script often carries the envelope sender cpaneluser@server.hostname. SPF then passes for the server's hostname, not the customer's domain, so DMARC depends on DKIM alone.
How do you check reverse DNS and the HELO name?
Compare the PTR record of the sending IP, the server's hostname and the name Exim announces when it connects (the HELO or EHLO name). All three should be one name, and that name should resolve back to the IP.
hostname -f dig +short -x 203.0.113.10 dig +short A server.example.com exim -bP primary_hostname
The second command should print the hostname and the third the IP. The fourth prints Exim's own name for the server. What cPanel announces is set in the Exim Configuration Manager: by default it uses the IP's reverse DNS name when one exists, and it can read a per-domain name from /etc/mailhelo. Confirm from outside too: the top Received: line of your test message shows the announced name, then the name and IP the receiver looked up. Your server provider sets the PTR record, not WHM. An IPv6 address on that line needs its own PTR record and SPF coverage.
How do you check SPF, DKIM and DMARC records with dig?
Query three TXT records for the sending domain: the domain itself for SPF, the DKIM selector, and _dmarc.
dig +short TXT example.com dig +short TXT default._domainkey.example.com dig +short TXT _dmarc.example.com
Working records look like this, with the DKIM key shortened:
"v=spf1 +a +mx +ip4:203.0.113.10 ~all" "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A..." "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
- SPF. Exactly one record may start with
v=spf1; two producespf=permerror. It must cover the server's sending IP and any outside service that sends as the domain. - DKIM. cPanel signs with the selector
default. An empty answer means the key was never published, common when DNS is hosted away from the server. - DMARC.
p=noneonly monitors;quarantineandrejecttell receivers what to do with failures. Start withnoneand aruaaddress for the reports.
The Email Deliverability interface, in cPanel and in WHM, checks these records per domain and flags problems. When DNS is hosted elsewhere, copy the suggested records there. On a new server this belongs in the build: see cPanel and WHM setup.
How do you check whether the cPanel server IP is blacklisted?
Look the sending IP up on public DNS blocklists (still often called blacklists) and read any bounce in full, because it usually names the list or provider that refused the mail. Take the IP from the Received: line of your test message, which may not be the server's main address.
- Multi-list lookup sites query many DNS blocklists for one IP. The lists do not carry equal weight.
- The list operator's own lookup page is the authoritative answer for that list and the place to request removal.
- Mailbox providers' postmaster pages matter because large providers keep their own reputation data. An IP can be clean on every public list and still be filtered by one provider.
- Your own log. In
/var/log/exim_mainlog,**marks a failed delivery and==a deferred one, with the remote server's reply on the same line.
How do you find the account that is sending spam?
Start with the Exim queue, because a spam run nearly always leaves a queue far larger than normal.
exim -bpc
exim -bp | exiqsumm
exim -bp | awk '$4 ~ /^</ {print $4}' | sort | uniq -c | sort -rn | head
exim -Mvh MESSAGE_ID
exim -bpc prints the number of queued messages. exiqsumm groups the queue by destination domain. The third line counts queued messages per envelope sender, which usually puts the culprit at the top. exim -Mvh prints one message's headers. An -auth_id line names a mailbox that logged in over SMTP, which usually means a stolen password. A -local message with a cPanel username on the -ident line came from a script in that account, and an X-Source-Dir header, where cPanel adds one, names the directory.
Then count senders in the log:
grep '<=' /var/log/exim_mainlog | grep -o 'A=dovecot_[a-z]*:[^ ]*' | sort | uniq -c | sort -rn | head
grep 'cwd=/home' /var/log/exim_mainlog | awk -F'cwd=' '{print $2}' | awk '{print $1}' | sort | uniq -c | sort -rn | head
The first line counts messages per authenticated mailbox. The second counts the directories scripts sent mail from, if your Exim log records the working directory. WHM shows much of this without a shell, in Mail Queue Manager and Mail Delivery Reports.
How do you fix it once you know the source?
Close the hole first, then clean up, then ask for delisting. Delist too early and the IP is listed again, and a repeat listing is usually harder to remove.
- Stop the source. For a hacked mailbox, change its password and remove any forwarders or filters the intruder added. For a script, disable the file and find how it got there: an outdated CMS, a vulnerable plugin, or a stolen FTP or cPanel password. Suspend the account if you cannot contain it quickly.
- Clear the queue carefully. Other customers' real mail is in the same queue, so count first, then remove only the spam sender's messages:
exiqgrep -c -f 'user@example.com' exiqgrep -i -f 'user@example.com' | xargs exim -Mrm
- Set hourly limits per domain. In WHM, open Tweak Settings and the Mail tab. Max hourly emails per domain is unlimited by default, so set a number an ordinary customer never reaches. Maximum percentage of failed or deferred messages a domain may send per hour stops a domain whose mail is mostly bouncing. Edit a Package and Modify an Account set a limit for one package or account.
- Request delisting. Use each list's own removal form and say what you found and changed. Some lists drop an entry once the spam stops; others need a request.
- Tell the affected customers what happened and which passwords changed.
How long does recovery take, and what can nobody promise?
Reputation on a shared IP recovers slowly, and nobody can promise inbox placement. After the spam stops, receiving providers need to see clean mail from the IP before they trust it again. Expect days or weeks, not hours; each provider decides for itself.
Passing SPF, DKIM and DMARC makes a message eligible for the inbox. It does not put it there. Providers also weigh content, links, complaint rates and how recipients treat your mail. A new IP is not a clean fix either: it has no reputation, may carry a bad history of its own, and is listed just as fast if the hole is still open.
How do you stop it from happening again?
Limit what one account can send, get told early, and keep your own system mail off the shared reputation.
- Limits. Keep the hourly and failed-message limits on, and state the hourly limit in your terms.
- Queue alerts. Alert when
exim -bpcpasses a threshold well above your normal queue, as in my server monitoring work. - A separate path for transactional mail. Send invoices, password resets and other system mail through a dedicated IP or an external relay service, so one customer's mistake cannot hold up your billing emails.
- DMARC reports. Read the aggregate reports sent to your
ruaaddress. They list the IPs that sent mail as your domain and whether each passed. - Fewer ways in. Strong mailbox passwords, patched sites and a maintained firewall are covered in server security hardening and my firewall and hardening checklist.
What is the checklist for cPanel email going to spam?
Work down this list in order, and send a new test message after each fix.
- Test message sent;
spf,dkimanddmarcresults read from its headers. - PTR record, hostname and HELO name match, and the hostname resolves to the IP.
- The domain has one SPF record, a published DKIM key and a DMARC record.
- Sending IP checked on public blocklists; bounces read for the list name.
- Queue size checked; top senders and sending scripts identified.
- Source closed: password changed or script removed, and the way in fixed.
- Spam removed from the queue by sender, not with a full flush.
- Hourly limits set in WHM.
- Delisting requested after the fix; queue alert and DMARC reports in place.
If mail still lands in spam after this list, I do this work as a web hosting expert: I read the server first, then send a written report of what I found and changed. I cannot promise the inbox, and neither can anyone else.