The mystery of deleted files: tracking removals across cPanel, FTP, and PHP application layers
Back to blog

The mystery of deleted files: tracking removals across cPanel, FTP, and PHP application layers

Updated 9/20/2026 · Published 9/16/2026 · 4 min · Servers

Few incidents cause more confusion in production environments than the sudden disappearance of files. I recently investigated a real-world case: a critical subfolder hosting essential files vanished from a server between the 1st and 6th of the month.

The immediate question was: who deleted it, how was it deleted, and where is the log evidence?

Here is the step-by-step forensic investigation across the cPanel web interface, Pure-FTPd logs, and the PHP application layer.

1. First layer: cPanel file manager audit#

The initial hypothesis was checking whether an operator removed the files via cPanel's web File Manager.

Checking the cPanel access log:

grep "sub.domain.com.br" /usr/local/cpanel/logs/access_log

The search returned entries such as:

192.0.2.10 - userapp [06/04/2026:14:28:26 -0000] "GET /cpsess6428448619/json-api/cpanel?cpanel_jsonapi_module=Fileman&cpanel_jsonapi_func=listfiles&cpanel_jsonapi_apiversion=2&needmime=1&dir=%2fhome%2fuserapp%2fpublic_html%2fsub.domain.com.br&showdotfiles=1 HTTP/1.1" 200 0 ...

Log entry breakdown:#

This entry does not indicate a deletion. The listfiles function merely performs directory reads (getdents64 or stat) to render files in the browser.

Real deletions via web UI or API appear with specific verbs:

Scanning the log for actual deletion verbs:

grep "06/04/2026" /usr/local/cpanel/logs/access_log | grep "userapp" | grep -E "fileop|delete|unlink|trash"

Repeating this search across the entire time window returned zero deletion records. No user deleted files through cPanel's web interface.

2. Second layer: pure-ftpd log inspection#

If the web UI did not trigger the deletion, the next logical vector is FTP. On cPanel servers running Pure-FTPd, logs are found in:

In the FTP protocol, file deletion is recorded via the DELE command. In xferlog (positional format log), deletions appear as transfers with direction d — the reliable audit trail for DELE commands; syslog (/var/log/messages) logs high-level connections and session events, meaning individual DELE commands may not always produce a dedicated line.

Auditing system messages:

grep "06/Apr/2026" /var/log/messages | grep "userapp" | grep "DELE"

And checking xferlog (where an isolated d flag denotes a deletion):

grep "userapp" /var/log/xferlog | grep " d "

Extending this check across the entire incident window yielded zero DELE events. No authenticated FTP session cleared the folder.

3. The application factor: direct PHP execution#

With File Manager and FTP ruled out, an architectural clue remained: the missing subfolder was nested beneath a WordPress (PHP) webroot.

When a PHP script deletes a file, it calls the native unlink() function, invoking the SYS_unlinkat syscall directly in the Linux Kernel.

The blind spot: Because neither the cPanel daemon nor the FTP server participates in this operation, the deletion is completely invisible in access_log and messages. The trail exists only in web server logs (Apache domlogs), PHP error logs, or kernel auditing frameworks.

4. Application layer forensic workflow#

To identify root cause when PHP executes file deletions:

A. Auditing suspicious web requests in Apache domlogs#

If a plugin, script, or automated endpoint was triggered over HTTP to delete files, it created an access entry (typically a POST):

grep "06/Apr/2026" /etc/apache2/logs/domlogs/domain.com.br | grep -E "POST|DELETE" | awk '{print $1, $6, $7}' | sort | uniq -c | sort -nr

Look for POST calls to anomalous scripts inside upload directories (wp-content/uploads/).

B. Pinpointing the incident window with the first HTTP 404#

Apache domlogs are chronological and retain continuous access history. To find the exact second files vanished without pulling weeks-old 404s from earlier periods, delimit the incident time window before inspecting the earliest records:

awk '$4 >= "[01/Apr/2026:" && $4 < "[07/Apr/2026:" && $8 == "404"' \
  /etc/apache2/logs/domlogs/sub.domain.com.br | head -n 10

With the timestamp of the first 404 inside the incident window, isolate that precise second across Apache logs to correlate what scripts were executing concurrently.

C. Checking scheduled cronjobs#

Many cleanup routines run in the background via cron:

crontab -l -u userapp
ls -la /var/spool/cron/userapp

D. Kernel syscall auditing (auditd)#

If auditd was configured to watch the directory:

ausearch -f /home/userapp/public_html/sub.domain.com.br -i

auditd captures the exact syscall, logging the PID and the binary path (/usr/bin/php or /usr/sbin/httpd) responsible for the removal.

E. Recovering files with open descriptors (lsof)#

If files were recently deleted but an active process still holds them open, the inode remains allocated in kernel memory:

lsof +L1 | grep "userapp"

If Apache or PHP-FPM holds the file descriptor open, you can recover data directly from /proc/<PID>/fd/<FD>.

Conclusion: key forensic takeaways#

  1. listfiles in cPanel is read-only: Browsing directories in the web manager never modifies or removes files.
  2. Understand the syscall path: When deletions originate in PHP (unlink), execution passes straight to the kernel without touching cPanel or FTP logs.
  3. Use the first 404 as an anchor: The timestamp of the first 404 within the incident window in Apache domlogs pinpoints the exact moment of removal, allowing targeted correlation with suspicious POST requests or CMS cron tasks.

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments