diff --git a/src/__tests__/posthog-persistence.test.ts b/src/__tests__/posthog-persistence.test.ts
index 4f956459a..e80fb4614 100644
--- a/src/__tests__/posthog-persistence.test.ts
+++ b/src/__tests__/posthog-persistence.test.ts
@@ -1,6 +1,6 @@
///
import { PostHogPersistence } from '../posthog-persistence'
-import { SESSION_ID, USER_STATE } from '../constants'
+import { CLIENT_SESSION_PROPS, SESSION_ID, USER_STATE } from '../constants'
import { PostHogConfig } from '../types'
import Mock = jest.Mock
@@ -18,7 +18,7 @@ describe('persistence', () => {
let library: PostHogPersistence
afterEach(() => {
- library.clear()
+ library?.clear()
document.cookie = ''
referrer = ''
})
@@ -139,6 +139,7 @@ describe('persistence', () => {
distinct_id: 'test',
})}`
)
+ expect(document.cookie).not.toContain('test_prop')
lib.register({ otherProp: 'prop' })
expect(document.cookie).toContain(
@@ -155,6 +156,29 @@ describe('persistence', () => {
})}`
)
+ lib.register({
+ [CLIENT_SESSION_PROPS]: {
+ sessionId: 'sid',
+ props: {
+ initialPathName: '/some/pathname',
+ referringDomain: '$direct',
+ },
+ },
+ })
+ expect(document.cookie).toContain(
+ `ph__posthog=${encode({
+ distinct_id: 'test',
+ $sesid: [1000, 'sid', 2000],
+ $client_session_props: {
+ sessionId: 'sid',
+ props: {
+ initialPathName: '/some/pathname',
+ referringDomain: '$direct',
+ },
+ },
+ })}`
+ )
+
// Clear localstorage to simulate being on a different domain
localStorage.clear()
@@ -163,6 +187,13 @@ describe('persistence', () => {
expect(newLib.props).toEqual({
distinct_id: 'test',
$sesid: [1000, 'sid', 2000],
+ $client_session_props: {
+ sessionId: 'sid',
+ props: {
+ initialPathName: '/some/pathname',
+ referringDomain: '$direct',
+ },
+ },
})
})
})
diff --git a/src/posthog-core.ts b/src/posthog-core.ts
index b3eb68e90..55ea3f212 100644
--- a/src/posthog-core.ts
+++ b/src/posthog-core.ts
@@ -108,7 +108,7 @@ export const defaultConfig = (): PostHogConfig => ({
autocapture: true,
rageclick: true,
cross_subdomain_cookie: isCrossDomainCookie(document?.location),
- persistence: 'cookie',
+ persistence: 'localStorage+cookie', // up to 1.92.0 this was 'cookie'. It's easy to migrate as 'localStorage+cookie' will migrate data from cookie storage
persistence_name: '',
cookie_name: '',
loaded: __NOOP,
diff --git a/src/posthog-persistence.ts b/src/posthog-persistence.ts
index 7ccbec21f..71017e659 100644
--- a/src/posthog-persistence.ts
+++ b/src/posthog-persistence.ts
@@ -64,8 +64,10 @@ export class PostHogPersistence {
config['persistence'].toLowerCase() as Lowercase
) === -1
) {
- logger.critical('Unknown persistence type ' + config['persistence'] + '; falling back to cookie')
- config['persistence'] = 'cookie'
+ logger.critical(
+ 'Unknown persistence type ' + config['persistence'] + '; falling back to localStorage+cookie'
+ )
+ config['persistence'] = 'localStorage+cookie'
}
// We handle storage type in a case-insensitive way for backwards compatibility
const storage_type = config['persistence'].toLowerCase() as Lowercase
@@ -77,8 +79,15 @@ export class PostHogPersistence {
this.storage = sessionStore
} else if (storage_type === 'memory') {
this.storage = memoryStore
- } else {
+ } else if (storage_type === 'cookie') {
this.storage = cookieStore
+ } else {
+ // selected storage type wasn't supported, fallback to 'localstorage+cookie' if possible
+ if (localPlusCookieStore.is_supported()) {
+ this.storage = localPlusCookieStore
+ } else {
+ this.storage = cookieStore
+ }
}
this.user_state = 'anonymous'
diff --git a/src/session-props.ts b/src/session-props.ts
index 79021ae80..48bced994 100644
--- a/src/session-props.ts
+++ b/src/session-props.ts
@@ -12,7 +12,7 @@ import { SessionIdManager } from './sessionid'
import { PostHogPersistence } from './posthog-persistence'
import { CLIENT_SESSION_PROPS } from './constants'
-interface SessionSourceProps {
+export interface SessionSourceProps {
initialPathName: string
referringDomain: string // Is actually host, but named domain for internal consistency. Should contain a port if there is one.
utm_medium?: string
@@ -22,7 +22,7 @@ interface SessionSourceProps {
utm_term?: string
}
-interface StoredSessionSourceProps {
+export interface StoredSessionSourceProps {
sessionId: string
props: SessionSourceProps
}
diff --git a/src/storage.ts b/src/storage.ts
index c559b379c..b090e5892 100644
--- a/src/storage.ts
+++ b/src/storage.ts
@@ -1,6 +1,6 @@
import { _extend } from './utils'
import { PersistentStore, Properties } from './types'
-import { DISTINCT_ID, SESSION_ID, SESSION_RECORDING_IS_SAMPLED } from './constants'
+import { CLIENT_SESSION_PROPS, DISTINCT_ID, SESSION_ID, SESSION_RECORDING_IS_SAMPLED } from './constants'
import { _isNull, _isUndefined } from './utils/type-utils'
import { logger } from './utils/logger'
@@ -140,6 +140,12 @@ export const cookieStore: PersistentStore = {
'; SameSite=Lax; path=/' +
cdomain +
secure
+
+ // 4096 bytes is the size at which some browsers (e.g. firefox) will not store a cookie, warn slightly before that
+ if (new_cookie_val.length > 4096 * 0.9) {
+ logger.warn('cookieStore warning: large cookie, len=' + new_cookie_val.length)
+ }
+
document.cookie = new_cookie_val
return new_cookie_val
} catch (err) {
@@ -230,7 +236,7 @@ export const localStore: PersistentStore = {
// Use localstorage for most data but still use cookie for COOKIE_PERSISTED_PROPERTIES
// This solves issues with cookies having too much data in them causing headers too large
// Also makes sure we don't have to send a ton of data to the server
-const COOKIE_PERSISTED_PROPERTIES = [DISTINCT_ID, SESSION_ID, SESSION_RECORDING_IS_SAMPLED]
+const COOKIE_PERSISTED_PROPERTIES = [DISTINCT_ID, SESSION_ID, SESSION_RECORDING_IS_SAMPLED, CLIENT_SESSION_PROPS]
export const localPlusCookieStore: PersistentStore = {
...localStore,