Firefox
Firefox Session Data
Overview
Firefox maintains session state in compressed JSONLZ4 files within the sessionstore-backups/ directory. These files record all open windows and tabs, per-tab navigation history, scroll positions, pinned tabs, container tab assignments, and recently closed tabs and windows. Firefox updates these files periodically (approximately every 15 seconds) and on browser shutdown, making them a near-real-time snapshot of the user's browsing state.
Session data is forensically valuable because it captures the user's active browsing context at the time of collection: what sites were open, which tab was active, full per-tab back/forward history, and recently closed content that may no longer appear in browsing history.
File Locations
| File | Path | Description |
|---|---|---|
| Current session | ~/Library/Application Support/Firefox/Profiles/{profile}/sessionstore-backups/recovery.jsonlz4 | Active session (updated every ~15 seconds) |
| Session backup | ~/Library/Application Support/Firefox/Profiles/{profile}/sessionstore-backups/recovery.baklz4 | Backup of previous recovery file |
| Previous session | ~/Library/Application Support/Firefox/Profiles/{profile}/sessionstore-backups/previous.jsonlz4 | Session from the last browser shutdown |
| Legacy session | ~/Library/Application Support/Firefox/Profiles/{profile}/sessionstore.jsonlz4 | Older Firefox versions |
All session files use the Mozilla JSONLZ4 compression format and must be decompressed before analysis.
File Format
JSONLZ4 Binary Structure
| Offset | Size | Description |
|---|---|---|
| 0 | 8 bytes | Magic header: mozLz40\x00 (hex: 6D 6F 7A 4C 7A 34 30 00) |
| 8 | 4 bytes | Original (decompressed) size, little-endian uint32 |
| 12 | N bytes | LZ4 block-compressed data |
Key implementation details:
- This is LZ4 block format, not the more common LZ4 frame format. Standard LZ4 frame decompressors will fail.
- The macfor collector caps decompressed output at 100 MB to prevent memory exhaustion.
- The decompressed content is standard JSON.
Decompressed Session JSON Structure
{
"windows": [
{
"tabs": [
{
"entries": [
{ "url": "https://example.com", "title": "Example" },
{ "url": "https://example.com/page2", "title": "Page 2" }
],
"index": 2,
"lastAccessed": 1706234567000,
"pinned": false,
"hidden": false,
"userContextId": 0,
"image": "https://example.com/favicon.ico"
}
],
"selected": 1,
"width": 1440,
"height": 900,
"screenX": 0,
"screenY": 25,
"sizemode": "maximized",
"_closedTabs": []
}
],
"selectedWindow": 1,
"session": {
"lastUpdate": 1706234567000,
"startTime": 1706200000000,
"recentCrashes": 0
},
"_closedWindows": []
}
Session Metadata
| Field | Description |
|---|---|
session.lastUpdate | When the session file was last written (Unix ms) |
session.startTime | When the current session began (Unix ms) |
session.recentCrashes | Number of recent session crashes |
selectedWindow | Index of the currently focused window (1-indexed) |
Window Fields
| Field | Description |
|---|---|
tabs | Array of open tabs in this window |
selected | Index of the active tab (1-indexed) |
width / height | Window dimensions in pixels |
screenX / screenY | Window position on screen |
sizemode | Window state: normal, maximized, minimized, fullscreen |
isPrivate | True if this is a private browsing window |
_closedTabs | Recently closed tabs (recoverable) |
Tab Fields
| Field | Description |
|---|---|
entries | Navigation history entries (back/forward list) |
index | Current position in entries array (1-indexed) |
lastAccessed | When the tab was last viewed (Unix ms) |
pinned | Whether the tab is pinned |
hidden | Whether the tab is hidden (e.g., by Tab Groups extensions) |
userContextId | Container tab ID (0 = default, 1+ = specific container) |
scroll | Scroll position within the current page |
image | Tab favicon URL |
Tab Entry Fields
| Field | Description |
|---|---|
url | Page URL |
title | Page title |
referrer | Referrer URL for this navigation |
subframe | Whether this is a subframe entry |
persist | Whether the entry persists across session restores |
Key Fields for Analysis
| Field | Forensic Significance |
|---|---|
Tab entries array | Full per-tab navigation history (back button sequence) |
Tab index | Current page in the entries sequence |
lastAccessed | When the user last viewed each tab |
pinned | Pinned tabs indicate frequently used, persistent sites |
userContextId | Identifies container tab usage (separate identities) |
_closedTabs | Recently closed tabs -- content the user dismissed |
_closedWindows | Recently closed windows |
isPrivate | Identifies private browsing windows (if session data captures them) |
session.recentCrashes | Non-zero values indicate browser instability |
Timestamps
Session file timestamps use Unix milliseconds (milliseconds since 1970-01-01 UTC).
lastAccessed = 1706234567000
Unix seconds = 1706234567000 / 1000 = 1706234567
Result = 2024-01-25T22:22:47Z
The recovery.jsonlz4 file's filesystem modification time also indicates when the session was last saved.
Analysis Notes
Reconstructing Active Browsing State
The session file captures a snapshot of what the user was doing:
- Which tabs were open: Each window's
tabsarray. - Which tab was active:
window.selected(1-indexed). - What the user was looking at: The
entries[index-1]of the selected tab. - How they got there: The full
entriesarray for each tab shows the navigation sequence.
Per-Tab Navigation History
Each tab's entries array is a complete back/forward history. The index field (1-indexed) indicates the current page. Entries before index are the back-button history; entries after are forward-button history (pages the user navigated back from).
For example, if a tab has 15 entries and index = 12, the user:
- Navigated through 12 pages (entries 1-12).
- Navigated back 3 times from later pages (entries 13-15 are in the forward history).
- Is currently viewing entry 12.
Recently Closed Tabs
The _closedTabs array within each window contains tabs the user closed during the current session. This is a circular buffer with a limited capacity (typically 25 entries). Closed tabs include:
- The full navigation history of the closed tab.
- The time the tab was closed.
- The tab's position and attributes.
Container Tab Analysis
Non-zero userContextId values indicate the user was using Firefox container tabs:
| userContextId | Typical Container |
|---|---|
| 0 | Default (no container) |
| 1 | Personal |
| 2 | Work |
| 3 | Banking |
| 4 | Shopping |
| 5+ | Custom containers |
Container tabs maintain separate cookie jars, so the same site in different containers represents different logged-in identities.
Session File Precedence
When analysing, prioritise files in this order:
- recovery.jsonlz4: Most recent session state (updated every ~15 seconds while Firefox is running).
- recovery.baklz4: Backup of the previous recovery file (one update cycle behind).
- previous.jsonlz4: Session from the last clean browser shutdown.
If recovery.jsonlz4 is missing but previous.jsonlz4 exists, the browser may have crashed or been forcefully terminated.
Crash Detection
session.recentCrashes > 0: The browser crashed recently.- Presence of
recovery.jsonlz4withoutprevious.jsonlz4: Possible abnormal shutdown. - Very old
session.lastUpdate: Session file was not updated before shutdown.
Private Browsing Windows
Windows with isPrivate: true occasionally appear in session data, particularly in crash recovery scenarios. Private browsing windows are not normally persisted, so their presence indicates the session file was written during an unexpected shutdown while a private window was open.
Version Differences
| Version | Change |
|---|---|
| Firefox 56+ | JSONLZ4 format for session files (replaced JSON + gzip) |
| Firefox 57+ | _closedTabs and _closedWindows arrays for undo close |
| Firefox 72+ | userContextId for container tabs (Multi-Account Containers) |
The JSONLZ4 session format has been consistent across all supported Firefox versions (78+). The internal JSON schema evolves gradually with new optional fields.
Tool Support
| Tool | Capability |
|---|---|
| macfor | Full JSONLZ4 decompression, window/tab extraction, container context parsing (Pro module) |
| lz4jsoncat | Command-line JSONLZ4 decompressor (part of mozilla-lz4 tools) |
| mozlz4-edit | Web-based JSONLZ4 editor/decompressor |
| Python lz4 + custom script | Decompress with 8-byte magic header handling |
| AXIOM | Automated Firefox session extraction |
| jq | JSON processing after decompression |
Manual Decompression (Python)
import lz4.block
import json
with open("recovery.jsonlz4", "rb") as f:
magic = f.read(8)
assert magic == b"mozLz40\x00"
size = int.from_bytes(f.read(4), "little")
data = lz4.block.decompress(f.read(), uncompressed_size=size)
session = json.loads(data)