Skip to content

File accessUSB & devices

CUPS Print Jobs and Logs: macOS Printing Forensics

macOS prints through CUPS: spool control files, page_log and access_log show which documents were printed, by whom, on which printer and when.

Location
/private/var/spool/cups/ and /private/var/log/cups/
Proves
Which user printed which job (document name and application), to which printer, when, and how many pages or copies
Timestamps
IPP attributes time-at-creation / processing / completed in Unix epoch seconds; log lines in local time with UTC offset
Access
root for the spool and logs; printers.conf readable by root
Retention
Control files for up to the configured job history (default 500 jobs); data files usually removed after the job; logs rotate by size
Collection
mac_apt, ditto, Disk image

What it is

macOS prints through CUPS (Common UNIX Printing System), which Apple ships and maintains. Every job submitted from any app goes to cupsd, which writes a control file with the job's IPP attributes and, while it is processed, a data file with the document. CUPS also writes access and page logs. Together they are the macOS equivalent of the Windows spool .SPL / .SHD files.

In data theft cases, printing is an exfiltration channel that bypasses USB and network controls, and the job name often carries the document title.

Where it lives

SourcePathNotes
Control files/private/var/spool/cups/c<job-id>IPP attributes per job, kept for job history
Data files/private/var/spool/cups/d<job-id>-<n>Document as sent (PDF, PostScript or raster); normally deleted after printing
Job cache/private/var/spool/cups/cache/job.cacheSummary of known jobs
Access log/private/var/log/cups/access_logHTTP/IPP requests to cupsd, with user and operation
Page log/private/var/log/cups/page_logOne line per page or job when page logging is active
Error log/private/var/log/cups/error_logFilter and backend messages; level set in cupsd.conf
Printers/private/etc/cups/printers.conf, /private/etc/cups/ppd/Configured queues, device URIs (IP, USB, AirPrint)
Server config/private/etc/cups/cupsd.conf, cups-files.confRetention and logging settings

What it proves

  • A user printed a job, with its job name (usually the document or web page title), the originating application, the destination printer and the number of copies.
  • When the job was created, processed and completed, and whether it finished, was cancelled or aborted.
  • The printer's identity and connection (network IP, USB, AirPrint, "Save as PDF" does not go through a physical printer queue).
  • If a data file survived, the printed content itself.

Key fields

Control files are binary IPP messages. Useful attributes, as extracted by mac_apt's PRINTJOBS plugin:

AttributeMeaning
job-id, job-uuidJob identity
job-nameDocument title as supplied by the app
job-originating-user-namemacOS account that printed
job-originating-host-namelocalhost or a remote client for shared printers
printer-uri, DestinationPrinterIDQueue and printer
com.apple.print.JobInfo.PMApplicationNameApplication that printed
document-formatMIME type of the data
copies, job-media-sheets-completedCopies requested, sheets printed
time-at-creation, time-at-processing, time-at-completedUnix epoch seconds
job-state3 pending to 9 completed; 7 cancelled, 8 aborted

page_log lines follow the PageLogFormat directive; by default they include printer, user, job ID, date, page or total, copies, billing info, originating host and job name.

Timestamps

IPP times are Unix epoch seconds (UTC). CUPS log lines use the Common Log Format with a local time and UTC offset, for example [30/Sep/2026:10:14:02 +0200]. The birth time of a control file matches job creation.

Retention

  • CUPS keeps job history (control files) up to MaxJobs, 500 by default, and for the time set by PreserveJobHistory; older control files are purged as new jobs arrive.
  • Data files are kept only as long as PreserveJobFiles allows (CUPS documents a default of one day in recent versions); Apple's defaults may differ, so check cupsd.conf on the image.
  • Logs rotate when they reach MaxLogSize. Depending on configuration, the error log may go to the system log instead of error_log.

Collection

sudo ditto /private/var/spool/cups ./case/cups_spool
sudo ditto /private/var/log/cups ./case/cups_logs
sudo ditto /private/etc/cups ./case/cups_etc
lpstat -W completed -o > ./case/lpstat_completed.txt   # live only

Parsing

python3 mac_apt.py -o out E01 image.E01 PRINTJOBS

grep -h 'Print-Job\|Send-Document\|Create-Job' ./case/cups_logs/access_log*
cat ./case/cups_logs/page_log*

lpstat -W completed -o lists completed jobs a live system still remembers. For surviving d files, check the header (%PDF, %!PS) and open a copy with a suitable viewer.

Investigator tips

  • Job names from browsers are page titles; from Office and Preview they are file names. Match them with Office MRU and Recent Items.
  • A new printer queue with a USB device URI appearing just before large jobs is worth correlating with USB devices.
  • job-originating-host-name other than localhost means the Mac shared its printer: other machines printed through it.
  • "Save as PDF" in the print dialog normally does not create a CUPS job; look for the resulting PDF and its file system times instead.

See also