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
Tools
Compare all tools- mac_aptCLI · open source
- lpstatCLI · built into macOS
- grepCLI · built into macOS
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
| Source | Path | Notes |
|---|---|---|
| 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.cache | Summary of known jobs |
| Access log | /private/var/log/cups/access_log | HTTP/IPP requests to cupsd, with user and operation |
| Page log | /private/var/log/cups/page_log | One line per page or job when page logging is active |
| Error log | /private/var/log/cups/error_log | Filter 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.conf | Retention 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:
| Attribute | Meaning |
|---|---|
job-id, job-uuid | Job identity |
job-name | Document title as supplied by the app |
job-originating-user-name | macOS account that printed |
job-originating-host-name | localhost or a remote client for shared printers |
printer-uri, DestinationPrinterID | Queue and printer |
com.apple.print.JobInfo.PMApplicationName | Application that printed |
document-format | MIME type of the data |
copies, job-media-sheets-completed | Copies requested, sheets printed |
time-at-creation, time-at-processing, time-at-completed | Unix epoch seconds |
job-state | 3 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 byPreserveJobHistory; older control files are purged as new jobs arrive. - Data files are kept only as long as
PreserveJobFilesallows (CUPS documents a default of one day in recent versions); Apple's defaults may differ, so checkcupsd.confon the image. - Logs rotate when they reach
MaxLogSize. Depending on configuration, the error log may go to the system log instead oferror_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-nameother thanlocalhostmeans 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.