How to Track Who Deleted Emails in WHM/cPanel (With Ready-to-Run Audit Script)

cPanel WHM email deletion audit script LinuxBear.com

Running a cPanel/WHM server means handling customer email issues on a daily basis. One of the most common (and frustrating) problems? A customer suddenly reports that all of their emails are missing. Was it deleted by a user? Did a POP3 client download and remove them? Or was there unauthorized access?

In this guide, we’ll walk you through:

  • How to troubleshoot missing emails in cPanel/WHM
  • How to check Dovecot, Exim, and cPanel logs
  • A ready-to-run Linux shell script that audits email deletions and logins
  • SEO-friendly best practices for system admins

Why Emails Disappear in cPanel

Before blaming the server, it’s important to understand why emails may vanish:

  1. POP3 Download – If a user configures their mail client (Outlook, Thunderbird, etc.) with POP3, it downloads and removes mail from the server.
  2. IMAP Deletion – If a user deletes mail from their phone or client, IMAP sync removes it everywhere.
  3. Webmail (Roundcube/Horde) – Users may delete emails via Webmail.
  4. Filters/Forwarders – Misconfigured filters may silently delete or forward mail.
  5. Compromised Account – If the password was leaked, an attacker might have accessed and deleted emails.

Step-by-Step Troubleshooting

You can check mail logs manually:

# Check if emails were expunged (deleted via IMAP)
grep 'Expunge' /var/log/maillog | grep 'user@domain.com'


# Check POP3 activity
grep 'POP3' /var/log/maillog | grep 'user@domain.com'


# Check Webmail & cPanel access logs
grep 'user@domain.com' /usr/local/cpanel/logs/access_log

But running all these commands repeatedly is painful. That’s why we built a mail audit script.

Mail Audit Script for cPanel/WHM

Here’s a complete script that scans current and rotated logs for IMAP deletions, POP3 downloads, and Webmail logins. It even summarizes the IP addresses involved.

Save as /root/mail_audit.sh, make executable, then run with the target email (and optionally the cPanel username if you know it).

bash /root/mail_audit.sh -e user@domain.com -u CPANELUSER
#!/usr/bin/env bash
# mail_audit.sh — Audit who deleted/downloaded emails for a cPanel mailbox
# Works on WHM/cPanel servers using Dovecot/Exim. Scans current + rotated logs.
# Usage:
#   ./mail_audit.sh -e user@domain.com [-u CPANEL_USERNAME]
#
# Examples:
#   ./mail_audit.sh -e info@example.com
#   ./mail_audit.sh -e sales@example.com -u example

set -euo pipefail

EMAIL=""
CPUSER=""

while getopts ":e:u:" opt; do
  case $opt in
    e) EMAIL="$OPTARG" ;;
    u) CPUSER="$OPTARG" ;;
    \?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
    :) echo "Option -$OPTARG requires an argument." >&2; exit 1 ;;
  esac
done

if [[ -z "${EMAIL}" ]]; then
  echo "Usage: $0 -e user@domain.com [-u CPANEL_USERNAME]"
  exit 1
fi

DOMAIN="${EMAIL#*@}"
LOCALPART="${EMAIL%@*}"

ts() { date +"%Y-%m-%d %H:%M:%S"; }

log_files=(
  /var/log/maillog
  /var/log/maillog-*
  /var/log/dovecot
  /var/log/dovecot-info.log
  /var/log/dovecot*
  /var/log/exim_mainlog
  /var/log/exim_mainlog-*
  /usr/local/cpanel/logs/access_log
  /usr/local/cpanel/logs/access_log-*
  /var/cpanel/roundcube/log/*
)

# Also search per-account Roundcube logs if CPUSER provided
if [[ -n "${CPUSER}" ]]; then
  log_files+=("/home/${CPUSER}/logs/roundcube/*" "/home/${CPUSER}/etc/${DOMAIN}/logs/roundcube/*")
fi

# De-duplicate existing files
declare -A seen
existing_files=()
for f in "${log_files[@]}"; do
  for m in $f; do
    if [[ -e "$m" ]] && [[ -z "${seen[$m]:-}" ]]; then
      existing_files+=("$m")
      seen[$m]=1
    fi
  done
done

if [[ ${#existing_files[@]} -eq 0 ]]; then
  echo "[$(ts)] No log files found. Are you running on a WHM/cPanel server?"
  exit 1
fi

# Grep wrapper that auto zgreps .gz
g() {
  local pattern="$1"; shift
  local file="$1"
  if [[ "$file" =~ \.gz$ ]]; then
    zgrep -i --no-filename --line-number "$pattern" "$file" 2>/dev/null || true
  else
    grep -i --no-filename --line-number "$pattern" "$file" 2>/dev/null || true
  fi
}

separator() {
  echo "========================================================================"
}

echo
separator
echo "[ $(ts) ] MAIL AUDIT REPORT for ${EMAIL}"
separator
echo

# 1) IMAP Expunge / Delete events (Dovecot)
echo "1) IMAP delete events (Dovecot 'expunge'/'deleted'):"
echo "-----------------------------------------------------------------------"
expunge_tmp=$(mktemp)
for f in "${existing_files[@]}"; do
  g "expunge|deleted|delete" "$f" | grep -i "$EMAIL" || true
done | tee "$expunge_tmp"
if [[ ! -s "$expunge_tmp" ]]; then
  echo "No IMAP delete/expunge lines found for ${EMAIL}."
fi
echo

# 2) POP3 downloads (messages pulled from server)
echo "2) POP3 access (client may download & remove from server):"
echo "-----------------------------------------------------------------------"
pop_tmp=$(mktemp)
for f in "${existing_files[@]}"; do
  g "pop3" "$f" | grep -i "$EMAIL" || true
done | tee "$pop_tmp"
if [[ ! -s "$pop_tmp" ]]; then
  echo "No POP3 activity found for ${EMAIL}."
fi
echo

# 3) IMAP logins (to correlate IPs)
echo "3) IMAP logins/sessions (Dovecot):"
echo "-----------------------------------------------------------------------"
imap_tmp=$(mktemp)
for f in "${existing_files[@]}"; do
  g "imap" "$f" | grep -i "$EMAIL" || true
done | tee "$imap_tmp"
if [[ ! -s "$imap_tmp" ]]; then
  echo "No IMAP login/session lines found for ${EMAIL}."
fi
echo

# 4) Roundcube/Webmail logs
echo "4) Roundcube/Webmail logins/errors:"
echo "-----------------------------------------------------------------------"
rc_tmp=$(mktemp)
for f in "${existing_files[@]}"; do
  case "$f" in
    *roundcube*|*/access_log*|*cpanel* )
      g "$EMAIL|$LOCALPART@$DOMAIN|$LOCALPART%40$DOMAIN" "$f" || true
      ;;
  esac
done | tee "$rc_tmp"
if [[ ! -s "$rc_tmp" ]]; then
  echo "No Roundcube/Webmail entries found for ${EMAIL}."
fi
echo

# 5) Exim deliveries/forwards (to check if mail was delivered/forwarded elsewhere)
echo "5) Exim deliveries/forwards for this address:"
echo "-----------------------------------------------------------------------"
exim_tmp=$(mktemp)
for f in "${existing_files[@]}"; do
  case "$f" in
    *exim_mainlog* )
      g "$EMAIL" "$f" || true
      ;;
  esac
done | tee "$exim_tmp"
if [[ ! -s "$exim_tmp" ]]; then
  echo "No Exim entries found for ${EMAIL}."
fi
echo

# 6) cPanel/Webmail access logs (who logged in via webmail/cPanel UI)
echo "6) cPanel/Webmail access (IPs, timestamps):"
echo "-----------------------------------------------------------------------"
cp_tmp=$(mktemp)
for f in "${existing_files[@]}"; do
  case "$f" in
    */access_log* )
      # Match either full email or percent-encoded form in URLs
      g "$EMAIL|$LOCALPART%40$DOMAIN" "$f" || true
      ;;
  esac
done | tee "$cp_tmp"
if [[ ! -s "$cp_tmp" ]]; then
  echo "No cPanel/Webmail access lines for ${EMAIL}."
fi
echo

# 7) Summaries: IPs involved in deletes / logins
ip_re='([0-9]{1,3}\.){3}[0-9]{1,3}'

summarize_ips () {
  local label="$1"; local file="$2"
  echo "Top IPs for ${label}:"
  echo "-----------------------------------------------------------------------"
  if [[ -s "$file" ]]; then
    # Try to extract IPv4s and count
    grep -Eo "$ip_re" "$file" | sort | uniq -c | sort -nr | head -20
  else
    echo "No data."
  fi
  echo
}

echo "7) IP Summaries"
separator
summarize_ips "IMAP delete/expunge" "$expunge_tmp"
summarize_ips "POP3 access" "$pop_tmp"
summarize_ips "IMAP logins" "$imap_tmp"
summarize_ips "Roundcube/Webmail + cPanel access" "$rc_tmp"
summarize_ips "cPanel/Webmail access_log" "$cp_tmp"

# 8) Hints
echo "Hints:"
echo "- If you see 'expunge' with an IP: that's a delete via IMAP from that IP/client."
echo "- POP3 lines with your email indicate a client likely downloaded mail (often removing from server)."
echo "- Webmail/cPanel access lines show who logged into the UI and from which IP."
echo "- Cross-check unfamiliar IPs with the customer. If unknown, reset the mailbox password and re-scan."
echo
separator
echo "[ $(ts) ] End of report for ${EMAIL}"
separator

How to run

sudo bash /root/mail_audit.sh -e user@domain.com
# If you know the cPanel username for better Roundcube paths:
sudo bash /root/mail_audit.sh -e user@domain.com -u CPANELUSER

What This Script Reports

  • IMAP Deletions (Expunge events) → Confirms if emails were deleted via IMAP, with IP address.
  • POP3 Downloads → Indicates if emails were pulled and removed by a mail client.
  • Webmail & cPanel Access → Shows who logged in via Webmail, from where, and when.
  • IP Summaries → Lists the top IP addresses that accessed the mailbox.

Common Errors

/root/mail_audit.sh: line 10: $'\r': command not found : invalid option nameline 11: set: pipefail

Ah that error ($'\r': command not found) happens because the script was saved with Windows (CRLF) line endings, not Linux (LF).

Linux shell (bash) chokes on those hidden \r carriage return characters

Fix

Option 1: Convert the file

Run:

dos2unix /root/mail_audit.sh

If dos2unix isn’t installed:

yum install dos2unix -y # CentOS/RHEL/AlmaLinux
apt install dos2unix -y # Debian/Ubuntu

Then re-run:

bash /root/mail_audit.sh -e user@domain.com -u CPANELUSER

Option 2: Use sed (no install needed)

If you can’t install dos2unix:

sed -i 's/\r$//' /root/mail_audit.sh

Option 3: When Copy-Pasting

If you paste directly into nano or vim on the server, make sure your editor is set to Unix line endings.

Example:

nano -c /root/mail_audit.sh
# paste script, save with CTRL+O then CTRL+X

After conversion, the errors will disappear and the script will run properly.

DANYAL

About the author

DANYAL

I’m Danyal Saleem, an IT professional with over 10 years of experience in Linux, Windows servers, VMware, and cloud computing. I also work with popular hosting and management tools like WHM/cPanel, Plesk, WHMCS, and SolusVM2, as well as networking technologies such as pfSense, Cisco, and

Read full bio →

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top