When a server suddenly sends far more email than usual, the first question is who is sending it. Exim's main log records every message that arrives for delivery, so a short script can count outgoing mail by sender domain over the last few days and point you at the account to look at. This guide gives a tested script for your own cPanel or DirectAdmin server, explains how it works, and shows what to do if you are on shared hosting instead.
On your own server, read the Exim main log and its rotated copies, keep only arrival lines (<=) inside a look-back window, keep only outbound mail (sent by a local script with P=local or by a logged-in mailbox with A=), and count the sender's domain. The script in section 3 does this in one awk pass and can be limited to one cPanel user. The sender is the address after <=; S= is the message size, not the sender. On Domain India shared hosting the mail logs are not visible to you: open a ticket and support will check them.
1. What a look-back window is for
A look-back window limits the analysis to a recent period, such as the last 24 hours or 7 days, instead of the whole log history. That makes it useful for:
- confirming a spam outbreak and when it started;
- finding which domain or mailbox on the server is responsible;
- comparing a busy account's normal volume with today's.
It needs root access to the server's mail log, so it is for a server you administer. On shared hosting, see section 6.
2. Reading an Exim arrival line
Exim writes one line when a message arrives for delivery, marked <=, and further lines as it delivers it (=>), defers it (==) or fails (**). An arrival line looks like this:
2026-09-21 04:00:00 1tAbCd-000Xyz-9Q <= [email protected] H=(laptop) [203.0.113.5] P=esmtpsa A=dovecot_login:[email protected] S=4210 for [email protected]| Part | Meaning |
|---|---|
| 2026-09-21 04:00:00 | Date and time, in a form that sorts correctly as text |
| 1tAbCd-000Xyz-9Q | Exim's message ID |
| <= [email protected] | Arrival, and the sender's envelope address |
| U=alice | The local user that submitted it, for mail sent by a PHP script or cron job |
| A=dovecot_login:… | The mailbox that logged in to send it (A=login:… on DirectAdmin) |
| P=local or P=esmtpsa | How it arrived: from a local program, or over authenticated SMTP |
| S=4210 | Message size in bytes |
Mail arriving from the internet for your users has neither P=local nor A=, which is how the script tells outbound mail from inbound.
3. The script: top sending domains in the last N days
The log lives at /var/log/exim_mainlog on cPanel, with older days rotated to compressed files beside it; on DirectAdmin it is /var/log/exim/mainlog. Run as root:
#!/bin/bash
# Top sender domains for outbound mail in the last DAYS days.
DAYS=7
LOG=${LOG:-/var/log/exim_mainlog} # DirectAdmin: LOG=/var/log/exim/mainlog
CPUSER=${CPUSER:-} # optional: limit to one local user
SINCE=$(date -d "$DAYS days ago" '+%F %T')
zcat -f "$LOG"* 2>/dev/null \
| awk -v since="$SINCE" -v user="$CPUSER" '
($1 " " $2) < since { next } # outside the window
$4 != "<=" || $5 == "<>" { next } # arrivals only, skip bounces
!(/ P=local / || / A=[a-z_]+:/) { next } # outbound only
user != "" && index($0, " U=" user " ") == 0 { next }
{ n = split($5, a, "@"); if (n == 2) cnt[tolower(a[2])]++ }
END { for (d in cnt) printf "%8d %s\n", cnt[d], d }
' | sort -rn | head -20Save it as top-senders.sh and run bash top-senders.sh, or CPUSER=alice bash top-senders.sh for one user's script-sent mail. Sample output:
24850 shop.example.in
3221 example.com
302 example.orgA domain sending tens of thousands of messages when it normally sends hundreds is the one to investigate.
How it works
zcat -f "$LOG"*reads the current log and the rotated.gzcopies together;-fpasses uncompressed files through unchanged.- The window check compares the log's date and time as text with a cutoff built by
date. Exim's timestamp format sorts correctly as text, so no date conversion is needed, and the script runs in anyawk. $4 != "<="keeps arrival lines only, so each message is counted once however many recipients it has.<>is a bounce generated by the server, not a sender.- The outbound filter keeps mail sent by a local program or a logged-in mailbox and drops inbound mail from other servers.
- The sender is field 5, the address straight after
<=. The script splits it at@and counts the domain.
Older versions of this script took the sender from the S= field. In Exim logs S= is the message size in bytes, so that script counted nothing useful. Always take the sender from the address after <=. If your server's log format has extra fields (for example a process ID after the time), check which field holds <= with one sample line and adjust $4 and $5.
4. Two useful variations
Where the mail went. Count remote deliveries by recipient domain, which shows whether a burst targets one provider:
zcat -f /var/log/exim_mainlog* | awk -v since="$(date -d '1 day ago' '+%F %T')" '
($1 " " $2) >= since && $4 == "=>" && / T=[a-z_]*remote_smtp / {
n = split($5, a, "@"); if (n == 2) cnt[tolower(a[2])]++ }
END { for (d in cnt) printf "%8d %s\n", cnt[d], d }' | sort -rn | headWhich script sent it (cPanel). cPanel logs the working directory of every program that calls sendmail on a cwd= line. Counting them points at the website folder behind script-sent spam:
zcat -f /var/log/exim_mainlog* | awk -v since="$(date -d '1 day ago' '+%F %T')" '
($1 " " $2) >= since && $3 ~ /^cwd=\/home\// { cnt[substr($3, 5)]++ }
END { for (d in cnt) printf "%8d %s\n", cnt[d], d }' | sort -rn | headTo count by logged-in mailbox instead of domain, see Mastering mail log analysis, and use exigrep with a message ID to follow one message from arrival to delivery.
5. What to do with the result
- Confirm it is abnormal.Compare with a normal day by changing
DAYSor the cutoff. A newsletter day looks like a spike too. - Mailbox or script?Lines with
A=point at a mailbox whose password may be stolen; lines withU=andP=localpoint at a website script or cron job. - Contain it.Change the mailbox password, or disable the abused form or script, then pause outgoing mail for that account while you clean up.
- Check the queue. `exim -bpexiqsumm` summarises what is still waiting; see Managing the Exim mail queue.
- Prevent a repeat.Set per-account sending limits, add a CAPTCHA to forms, and keep WordPress and plugins updated.
6. On Domain India shared hosting
Server mail logs cover every account on the server, so shared hosting customers can't read them, and the script above can't run in your account. If you suspect your account is sending spam, or a customer reports odd mail from your domain:
- change every mailbox password and check for forwarders and filters you didn't create;
- open a support ticket with the dates, the domain and a sample bounce; support can check the logs and tell you which mailbox or script sent the mail.
Outgoing mail is limited per account: 200 messages per hour on cPanel and 1,000 per day on DirectAdmin. The full clean-up routine is in How to investigate email spam and abuse problems.
7. Where Domain India fits
On a Domain India VPS you have root access and read your own mail logs, so everything in sections 2 to 5 applies. VPS plans are self-managed: you run the mail server, updates and monitoring yourself. See VPS plans. If you'd rather not run mail at all, Business Email provides managed mailboxes with DKIM signing on every message; see Business Email.
- 1 vCPU
- 2 GB DDR4 RAM
- 64 GB NVMe SSD Storage
- 2 TB Monthly Bandwidth
- Priced per mailbox - start with one
- Email at your own domain ([email protected])
- Add and remove mailboxes yourself
- Webmail with calendar, contacts and tasks
Prices on the cards exclude 18% GST.
Where is the Exim log on cPanel and DirectAdmin?
On cPanel it is /var/log/exim_mainlog, with older days rotated to compressed files beside it. On DirectAdmin it is /var/log/exim/mainlog. Reading them needs root access.
How do I find which domain is sending the most email on my server?
Take the arrival lines (marked <=) inside your look-back window, keep only mail sent by local programs (P=local) or logged-in mailboxes (A=), and count the domain of the address after <=. The script in this guide does this in one awk pass.
What does S= mean in an Exim log line?
S= is the message size in bytes. The sender's envelope address is the field straight after the <= arrival marker.
How can I tell whether spam came from a mailbox or a website script?
Arrival lines with A= were sent by a mailbox that logged in, which suggests a stolen password. Lines with U= and P=local were submitted by a local program, such as a PHP form or cron job; on cPanel the matching cwd= line shows the folder it ran from.
Can I read the mail logs on Domain India shared hosting?
No. The mail logs cover every account on the server, so customers can't read them. Open a support ticket with the dates, domain and a sample bounce, and support will check the logs for you.
How many emails can I send from Domain India shared hosting?
Outgoing mail is limited to 200 messages per hour per account on cPanel and 1,000 per day on DirectAdmin.
Ready to track down the sender? Read Mastering mail log analysis for your own server, or open a support ticket if you are on shared hosting and need the logs checked.
Send us the dates, the domain and a sample bounce, and we will check the mail logs and tell you which mailbox or script sent the mail.
Open a support ticket