cPanel email goes to spam for four main reasons: the message fails SPF, DKIM or DMARC; the server's reverse DNS does not match its hostname; the sending 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 one test message.
The server commands need root on a cPanel and WHM server running Exim. Reseller customers can do the header, DNS and blocklist checks.
What does each symptom usually point to?
Each symptom narrows the cause, so match yours first.
| Symptom | Likely cause | First check |
|---|---|---|
| One domain 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 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 | A public blocklist or the receiver'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 envelope sender is not the From domain | smtp.mailfrom against header.from |
| The queue holds thousands of messages | A hacked mailbox or script sending in bulk | exim -bp | exiqsumm |
What do Gmail and Yahoo require from a cPanel server?
Both require authentication, valid forward and reverse DNS and a low spam rate from every sender, and more from bulk senders. Google's email sender guidelines set the bulk line at more than 5,000 messages a day to Gmail accounts. Yahoo's sender requirements are close to identical.
| Requirement | All senders | Bulk senders |
|---|---|---|
| Authentication | SPF or DKIM | SPF and DKIM |
| DMARC | Recommended | Required. p=none is enough, and the From domain must align with the SPF or DKIM domain |
| Reverse DNS | Valid PTR and matching forward record for the sending IP | Same |
| Spam complaint rate | Below 0.3% | Below 0.3% |
| Unsubscribe | Not required | One-click unsubscribe on marketing mail. Yahoo asks you to honor it within 2 days |
| Other | RFC 5322 formatting. Google also requires TLS | Same |
Google adds that DKIM keys must be at least 1024 bits, with 2048 recommended, and that the sending IP must match the IP of the hostname in its PTR record. Your invoices may not be bulk mail, but one customer's newsletter on the same IP affects them.
How do you confirm the problem with one test message?
Send a plain message from an affected mailbox to a mailbox you control at a large provider, open the original message and read Authentication-Results. A healthy result at Gmail looks like this:
Authentication-Results: mx.google.com;
dkim=pass header.i=@example.com header.s=default;
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 signature against a public key in DNS. dmarc passes only when SPF or DKIM passes for the domain in the visible From address. Trouble reads as spf=softfail, dkim=none, dkim=fail or dmarc=fail. For website form mail, compare smtp.mailfrom with header.from. If the envelope sender uses the server's hostname, SPF passes for the wrong domain and DMARC depends on DKIM alone.
How do you check reverse DNS and the HELO name?
The PTR record of the sending IP, the server's hostname and the name Exim announces (the HELO or EHLO name) 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
In WHM's Exim Configuration Manager, Use the reverse DNS entry for the mail HELO/EHLO if available is on by default. Reference /etc/mailhelo for custom outgoing SMTP HELO is off by default and cannot be used while the reverse DNS option is on, according to the Exim Configuration Manager documentation. The PTR record itself is set by whoever owns the IP, usually your server provider, not in WHM. An IPv6 address that sends mail needs its own PTR record and SPF coverage.
How do you check SPF, DKIM and DMARC records?
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
- SPF. Exactly one record may start with
v=spf1; two producespf=permerror. It must cover the server's IP and every outside service that sends as the domain. - DKIM. cPanel's support article on DKIM selectors says cPanel uses
default._domainkeyand offers no option to change it. An empty answer usually means the key was never published, which is common when DNS is hosted elsewhere. - DMARC.
p=noneonly monitors. Start there, with aruaaddress for the aggregate reports.
cPanel's Email Deliverability interface checks DKIM, SPF, DMARC and the PTR record per domain, and WHM has its own version for the server hostname. The provider must enable it in Feature Manager. Its Repair button works only when the server controls the domain's DNS; otherwise copy the suggested records to the DNS provider. The suggested SPF record ends in ~all by default, and the DMARC section needs valid SPF and DKIM first. If PHP mail arrives unsigned on a server with the DSO handler, the documentation points to two Exim Configuration Manager options for mail from processes running as nobody.
How do you check whether the cPanel server IP is blacklisted?
Look up the sending IP on public DNS blocklists 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, since it may not be the server's main address.
- Multi-list lookup sites check many lists at once, but the lists do not carry equal weight. The operator's own lookup page is the authoritative answer.
- Postmaster tools from the large providers matter because they keep their own reputation data. An IP can be clean on every public list and still be filtered.
- 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 one 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 counts queued messages, and exiqsumm groups them by destination domain. The third line counts messages per envelope sender, which usually puts the culprit at the top. In exim -Mvh output, an -auth_id line names a mailbox that logged in over SMTP, which usually means a stolen password. An -ident line with a cPanel username points to a script in that account. Then count authenticated senders in the log:
grep '<=' /var/log/exim_mainlog | grep -o 'A=dovecot_[a-z]*:[^ ]*' | sort | uniq -c | sort -rn | head
If your Exim log records the working directory, grep 'cwd=/home' /var/log/exim_mainlog shows which directories scripts sent from. WHM's Mail Queue Manager and Mail Delivery Reports show much of this without a shell.
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.
- Stop the source. For a hacked mailbox, change its password and remove forwarders or filters the intruder added. For a script, disable the file and find how it got there, often an outdated CMS or plugin. Suspend the account if you cannot contain it quickly.
- Remove only the spam. Other customers' real mail is in the same queue, so count first, then remove by sender:
exiqgrep -c -f 'user@example\.com' exiqgrep -i -f 'user@example\.com' | xargs exim -Mrm
- Set hourly limits. In WHM, Tweak Settings, Mail tab, Max hourly emails per domain is Unlimited by default and counts only remote deliveries, per the Tweak Settings documentation. Maximum percentage of failed or deferred messages a domain may send per hour blocks a domain whose mail is mostly bouncing, together with a minimum count of failures. A package can set a lower hourly limit, never a higher one.
- Request delisting through each list's own form, saying what you found and changed.
- 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. Receiving providers need to see clean mail from the IP before they trust it again, and each one decides for itself. Expect days or weeks, not hours.
Passing SPF, DKIM and DMARC makes a message eligible for the inbox; it does not put it there. A new IP is not a clean fix either: it has no reputation and is listed just as fast if the hole is still open.
How do you stop it from happening again?
- Limits and alerts. Keep the hourly limits on, and alert when
exim -bpcpasses a threshold well above normal. - A separate path for your own mail. Yahoo recommends keeping bulk mail off the IPs you use for transactional mail. Send invoices and password resets through a dedicated IP or relay, so one customer cannot hold up your billing mail.
- DMARC reports. Read the aggregate reports at your
ruaaddress. They list every IP that sent as your domain and whether it passed. - Fewer ways in. Strong mailbox passwords, patched sites and a maintained firewall, as in my firewall and server hardening checklist and my server security hardening work.
If mail still lands in spam after all this, I review mail setups as part of my hosting infrastructure consulting, with a written report of what I found and changed. I cannot promise the inbox, and neither can anyone else.
Last reviewed 12 October 2026. Disclosure: I sell server, hosting and WHMCS development work, so I am not neutral. I am CTO at ElySpace, a freelance developer for Marmaids LLC, and founder of WHMCSPilot and MagizAI.