Google Analytics 4 Cookie Format Change: From GS1 to GS2 Explained
Overview
Google Analytics has introduced a new cookie format (v2) to their Cookie Stream Cookie that changes how tracking data is structured. This guide explains the differences between the old GS1 format and the new GS2 format, and demonstrates how to parse both versions
Why Did Google Change the Cookie Format?
Google's transition from the GS1 to GS2 cookie format wasn't arbitrary. Here are several strategic reasons behind this change:
1. Improved Extensibility
The new format with single-letter prefixes allows Google to easily add new tracking parameters without breaking existing implementations. With GS1's fixed positional format, adding new parameters would require updating all parsers to handle additional positions.
2. Self-Documenting Structure
The prefix system makes cookies more readable and debuggable. Instead of remembering that position 5 is "lastHitTimestamp", developers can see t1746825440
and understand what t
represents.
3. Version Control
The explicit GS2 prefix enables Google to:
4. Data Optimization
The dollar sign separator and prefix system can be more efficient:
5. Better Error Handling
With prefixed values, parsers can:
6. Alignment with Modern Standards
The key-value pair approach (prefix + value) aligns better with:
7. Align with other Google Cookies
These format updates aren't isolated to Google Analytics. Google is systematically updating various tracking mechanisms to:
Google has recently updated several other cookie formats as well, most notably the Google Click ID (gclid) cookie. These changes follow a similar pattern of modernization:
This evolution reflects Google's need for a more flexible, maintainable tracking system as Google Analytics continues to evolve and add new features.
Old Format (GS1)
The original Google Analytics cookie format uses a straightforward dot-separated structure:
GS1.1.1746825440.14.0.17468254406.0.0.295082955
Characteristics:
Structure Breakdown:

New Format (GS2)
Google Analytics has transitioned to a more flexible cookie format that uses prefixed values:
GS2.1.s1746825440$o14$g0$t1746825440$j60$l0$h295082955
Characteristics:
$
) as separators after the headerStructure Breakdown:

Prefix Meanings:
s
- Session IDo
- Session Numberg
- Session Engagedt
- Last Hit Timestampj
- Join Timerl
- Enhanced User ID Logged In Stateh
- Enhanced User ID (hash)d
- Join ID (not present in this example, appears in some cookies)Complete Parser Implementation
I've created a universal parser that handles both Google Analytics cookie formats. This parser is based on my previous work that originally handled only the GS1 format, which I've now adapted to automatically detect and parse both GS1 and GS2 versions.
The parser provides:
%24
)I'm providing both ES6 and ES5 versions to ensure compatibility across different JavaScript environments:
ES6 Version (Modern JavaScript)
const parseGoogleStreamCookie = str => {
const mapping = {
s: "sessionId",
o: "sessionNumber",
g: "sessionEngaged",
t: "lastHitTimestamp",
j: "joinTimer",
l: "enhancedUserIdLoggedInState",
h: "enhancedUserId",
d: "joinId"
};
const [version, , data, ...rest] = str.split('.');
const keys = ['s', 'o', 'g', 't', 'j', 'l', 'h', 'd'];
if (version === 'GS1') {
return Object.fromEntries(
keys.map((k, i) => [mapping[k], [data, ...rest][i] || ''])
);
}
return Object.fromEntries(
data.replace(/%24/g, '$')
.split('$')
.map(s => [mapping[s[0]] || s[0], decodeURIComponent(s.slice(1))])
);
};
ES5 Version (Legacy Browser/GTM Support)
function parseGoogleStreamCookie(str) {
var mapping = {
s: "sessionId",
o: "sessionNumber",
g: "sessionEngaged",
t: "lastHitTimestamp",
j: "joinTimer",
l: "enhancedUserIdLoggedInState",
h: "enhancedUserId",
d: "joinId"
};
var parts = str.split('.');
var version = parts[0];
var result = {};
var entries = [];
if (version === 'GS1') {
// GS1: Create entries from positional values
var values = parts.slice(2);
var keys = ['s', 'o', 'g', 't', 'j', 'l', 'h', 'd'];
for (var i = 0; i < keys.length; i++) {
entries.push([mapping[keys[i]], values[i] || '']);
}
} else {
// GS2: Create entries from prefixed values
var segments = parts[2].replace(/%24/g, '$').split('$');
for (var j = 0; j < segments.length; j++) {
var prefix = segments[j][0];
var value = decodeURIComponent(segments[j].slice(1));
entries.push([mapping[prefix] || prefix, value]);
}
}
// Build result object from entries
for (var k = 0; k < entries.length; k++) {
result[entries[k][0]] = entries[k][1];
}
return result;
}
Feel free to use either version based on your project's requirements.
The parser will return a consistent object structure with descriptive property names, making it easy to work with Google Analytics cookie data in your applications.