From 607f4fc9dfaa71b2ad2b9217d11b7a31a1aeac7d Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Mon, 30 Mar 2026 13:46:09 +0200 Subject: [PATCH 1/2] fix(browser): wrap sendBeacon body in Blob to set Content-Type header Fixes #3262. Since commit 2e46959e changed gzip bodies from Blob to ArrayBuffer, sendBeacon requests were sent without a Content-Type header. This causes failures when proxies/WAFs require Content-Type to be set. --- .changeset/warm-birds-glow.md | 5 +++++ packages/browser/src/__tests__/request.test.ts | 12 ++++++++---- packages/browser/src/request.ts | 6 ++++-- 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 .changeset/warm-birds-glow.md diff --git a/.changeset/warm-birds-glow.md b/.changeset/warm-birds-glow.md new file mode 100644 index 0000000000..9afff663a3 --- /dev/null +++ b/.changeset/warm-birds-glow.md @@ -0,0 +1,5 @@ +--- +'posthog-js': patch +--- + +fix: wrap sendBeacon body in Blob to ensure Content-Type header is set diff --git a/packages/browser/src/__tests__/request.test.ts b/packages/browser/src/__tests__/request.test.ts index f217c049d6..aebd76bc20 100644 --- a/packages/browser/src/__tests__/request.test.ts +++ b/packages/browser/src/__tests__/request.test.ts @@ -502,11 +502,15 @@ describe('request', () => { expect(mockedNavigator?.sendBeacon).toHaveBeenCalledWith( 'https://any.posthog-instance.com/?_=1700000000000&ver=1.23.45&compression=gzip-js&beacon=1', - expect.any(ArrayBuffer) + expect.any(Blob) ) - const arrayBuffer = mockedNavigator?.sendBeacon.mock.calls[0][1] as ArrayBuffer - - const result = new TextDecoder().decode(arrayBuffer) + const blob = mockedNavigator?.sendBeacon.mock.calls[0][1] as Blob + expect(blob.type).toBe('text/plain') + const result = await new Promise((resolve) => { + const reader = new FileReader() + reader.onload = () => resolve(reader.result as string) + reader.readAsText(blob) + }) expect(result).toMatchInlineSnapshot(` "��VJ��W�RJJ,R���+� diff --git a/packages/browser/src/request.ts b/packages/browser/src/request.ts index a35cf4b355..d0df123960 100644 --- a/packages/browser/src/request.ts +++ b/packages/browser/src/request.ts @@ -255,8 +255,10 @@ const _sendBeacon = (options: RequestWithOptions) => { try { const { contentType, body } = encodePostData(options) ?? {} - // sendBeacon requires a blob so we convert it - const sendBeaconBody = typeof body === 'string' ? new Blob([body], { type: contentType }) : body + // sendBeacon requires a Blob to set the Content-Type header correctly. + // Without wrapping, ArrayBuffer bodies are sent with no Content-Type, + // which can cause issues with proxies/WAFs that require it. + const sendBeaconBody = body instanceof Blob ? body : new Blob([body as BlobPart], { type: contentType }) navigator!.sendBeacon!(url, sendBeaconBody) } catch { // send beacon is a best-effort, fire-and-forget mechanism on page unload, From 062ab0146fa0dd6fdca2057dad79fa0304a89c68 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Mon, 30 Mar 2026 13:51:55 +0200 Subject: [PATCH 2/2] fix: guard against undefined body in sendBeacon to avoid sending literal 'undefined' --- packages/browser/src/request.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/browser/src/request.ts b/packages/browser/src/request.ts index d0df123960..b72bda98cc 100644 --- a/packages/browser/src/request.ts +++ b/packages/browser/src/request.ts @@ -255,10 +255,13 @@ const _sendBeacon = (options: RequestWithOptions) => { try { const { contentType, body } = encodePostData(options) ?? {} + if (!body) { + return + } // sendBeacon requires a Blob to set the Content-Type header correctly. // Without wrapping, ArrayBuffer bodies are sent with no Content-Type, // which can cause issues with proxies/WAFs that require it. - const sendBeaconBody = body instanceof Blob ? body : new Blob([body as BlobPart], { type: contentType }) + const sendBeaconBody = body instanceof Blob ? body : new Blob([body], { type: contentType }) navigator!.sendBeacon!(url, sendBeaconBody) } catch { // send beacon is a best-effort, fire-and-forget mechanism on page unload,