Safari History.db: macOS Browsing History
Safari History.db, profile databases, Downloads.plist and tab stores on macOS: which pages a user visited, when, and what was deleted.
- Location
- ~/Library/Safari/History.db
- Proves
- Which URLs a macOS user opened in Safari, when, through which redirects, and whether history was cleared
- Timestamps
- Mac absolute time (Cocoa, seconds since 2001-01-01 UTC), stored as REAL
- Access
- Full Disk Access for the reading process (TCC); root alone is not enough
- Retention
- User setting, one year by default or kept until cleared manually; download list defaults to one day
- Collection
- Aftermath, mac_apt, UAC
Tools
Compare all tools- Browser ForensicsIn browser
- mac_aptCLI · open source
- APOLLOCLI · open source
- sqlite3CLI · built into macOS
- plutilCLI · built into macOS
What it is
Safari keeps its browsing history in a SQLite database named History.db. Each unique URL gets one row in history_items and each individual page load gets one row in history_visits, so the database answers both "has the user ever been here" and "exactly when, and how many times". Since Safari 17 introduced profiles, the default profile still writes to ~/Library/Safari/History.db while every additional profile has its own History.db inside the Safari sandbox container.
Around the history database sit several companion files: Downloads.plist (the download list), SafariTabs.db (open tabs, tab groups and profile names), CloudTabs.db (tabs open on the user's other iCloud devices), RecentlyClosedTabs.plist and, on some versions, LastSession.plist. This page is the quick reference; the Safari forensics guide walks through the analysis in more depth.
Where it lives
| File | Path | Versions / notes |
|---|---|---|
| History (default profile) | ~/Library/Safari/History.db (+ -wal, -shm, -lock) | 10.10 Yosemite and later; History.plist before that |
| History (extra profiles) | ~/Library/Containers/com.apple.Safari/Data/Library/Safari/Profiles/<UUID>/History.db | Safari 17+ |
| Downloads | ~/Library/Safari/Downloads.plist | Binary plist |
| Tabs, tab groups, profile names | ~/Library/Containers/com.apple.Safari/Data/Library/Safari/SafariTabs.db | Safari 15+ container layout |
| iCloud tabs | ~/Library/Containers/com.apple.Safari/Data/Library/Safari/CloudTabs.db | Older releases kept it in ~/Library/Safari |
| Recently closed tabs | ~/Library/Safari/RecentlyClosedTabs.plist | Present when tabs were closed |
| Last session | ~/Library/Safari/LastSession.plist | Not present on every version |
| Top sites | ~/Library/Safari/TopSites.plist and per-profile TopSites.plist | |
| Tab snapshots | ~/Library/Containers/com.apple.Safari/Data/Library/Caches/com.apple.Safari/TabSnapshots/Metadata.db | Cached page thumbnails |
| Preferences | ~/Library/Containers/com.apple.Safari/Data/Library/Preferences/com.apple.Safari.plist | Older: ~/Library/Preferences/com.apple.Safari.plist |
Protection: ~/Library/Safari and the Safari container are TCC-protected (Mojave and later). A process without Full Disk Access gets Operation not permitted, even under sudo. On a mounted dead-box image TCC does not apply.
What it proves
- Which URLs were loaded in Safari and at what time, per visit.
- Whether a load succeeded (
load_successful), and the redirect chain that led to a landing page (redirect_source/redirect_destination). - Page titles as they were at visit time (
history_visits.title), and search terms inside search-result URLs. - That the user deliberately cleared history:
history_tombstonesrecords the cleared time range or URL. - Which files Safari downloaded and where they were saved (
Downloads.plist), while the list has not been cleared. - Which tabs were open, pinned or grouped, and which profile they belonged to (
SafariTabs.db).
It does not prove:
- That the person at the keyboard read the page, or that a visit happened on this Mac: iCloud history sync can bring in visits from other devices (see the
origincolumn, and validate its values on a reference system before relying on them). - Private Browsing activity: private windows do not write visits to
History.db. - Activity in other browsers (see Chrome and Firefox).
Key fields
| Table / file | Field | Meaning |
|---|---|---|
history_items | id, url | Unique URL (one row per URL) |
history_items | domain_expansion | Short site label derived from the domain |
history_items | visit_count | Aggregate visit counter |
history_visits | history_item | Foreign key to history_items.id |
history_visits | visit_time | Time of the visit (Mac absolute time, REAL) |
history_visits | title | Page title at visit time |
history_visits | load_successful, http_non_get | Load result, non-GET request flag |
history_visits | redirect_source, redirect_destination | IDs of linked visits in a redirect chain |
history_visits | origin, generation | Sync-related bookkeeping (local vs synced) |
history_tombstones | start_time, end_time, url | Range or URL removed by a history clear |
Downloads.plist | DownloadHistory array: DownloadEntryURL, DownloadEntryPath, DownloadEntryDateAddedKey, DownloadEntryDateFinishedKey | Source, destination, start and finish of each download |
Newer builds add columns (for example status_code in history_items, udid in history_tombstones). Run .schema on the actual file first.
Timestamps
visit_time, start_time and end_time are floating-point Mac absolute time: seconds since 2001-01-01 00:00:00 UTC. Add 978307200 to get a Unix timestamp. Plist dates in Downloads.plist are native plist date objects, which plutil -p prints in UTC.
SELECT datetime(v.visit_time + 978307200, 'unixepoch') AS visit_utc,
i.url, v.title, v.load_successful, v.redirect_source, v.redirect_destination
FROM history_visits v
JOIN history_items i ON i.id = v.history_item
ORDER BY v.visit_time;
Retention
- History is pruned according to the user's "Remove history items" setting in Safari settings. The default is after one year, and "Manually" keeps history until the user clears it.
- "Clear History" removes rows and leaves
history_tombstonesentries behind. - The download list has its own removal setting and is frequently empty: Velociraptor's Safari downloads artifact notes a 24-hour default. The downloaded files, their quarantine attributes and the quarantine database usually outlive the list.
- Deleted rows may survive for a while in SQLite free pages and in the
-walfile until a checkpoint or vacuum.
Collection
Grant Full Disk Access to Terminal or the collector first. Copy each database together with its -wal and -shm files.
# Live, per user, preserving metadata
ditto ~/Library/Safari /cases/host01/Safari
ditto ~/Library/Containers/com.apple.Safari/Data/Library/Safari /cases/host01/Safari-container
- UAC: its
safari.yamlartifact collectsHistory*,Downloads.plist,SafariTabs.db*,CloudTabs*,LastSession.plist,RecentlyClosedTabs.plistand more from both locations. - Aftermath (Jamf): collects the default-profile
History.db,Downloads.plist,Bookmarks.plist,LastSession.plistandUserNotificationPermissions.plist(no extensions, no extra profiles); run as root with Full Disk Access. - mac_apt: the
SAFARIplugin exports and parses the files from a disk image or live mount, including Safari 17 profile databases.
Parsing
sqlite3forHistory.db,SafariTabs.dbandCloudTabs.db. Work on a copy (with its-waland-shmfiles in the same folder), never on the evidence original, because opening a WAL database can checkpoint it.plutil -p Downloads.plistfor the download list.- mac_apt
SAFARIplugin: history, downloads, bookmarks, top sites, last session, recently closed tabs, iCloud tabs and tab snapshots in one table. - APOLLO: the
safari_historymodule queriesHistory.db; its KnowledgeC Safari modules add app usage context.
Find deliberate clearing:
SELECT datetime(start_time + 978307200, 'unixepoch') AS cleared_from_utc,
datetime(end_time + 978307200, 'unixepoch') AS cleared_to_utc,
url
FROM history_tombstones ORDER BY end_time;
Investigator tips
- Always process every profile: a user who keeps a separate "Work" or "Private" profile has a second
History.dbin the containerProfiles/<UUID>/folder. Map UUIDs to profile names throughSafariTabs.db. - Missing
-walfiles are the most common reason for "missing" recent visits. - Tie downloads to files on disk through the
com.apple.quarantineandkMDItemWhereFromsattributes and the quarantine events database. - Correlate visits with KnowledgeC app focus to show Safari was actually in the foreground at the time.
history_tombstonesrows timestamped just before an incident window are a strong anti-forensics indicator; record them even when the history itself is gone.- Treat
CloudTabs.dbentries as another device's activity until proven otherwise.