fix(browser): wrap sendBeacon body in Blob to ensure Content-Type header is set#3297
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@dustinbyrne, do you know a way to validate this? no idea how i'd test this locally |
Important Files Changed
Prompt To Fix All With AIThis is a comment left during a code review.
Path: packages/browser/src/request.ts
Line: 261
Comment:
**Undefined body case silently creates unexpected Blob content**
`encodePostData` can return `undefined` (when `options.data` is falsy), so after `const { contentType, body } = encodePostData(options) ?? {}`, `body` will be `undefined`. The `as BlobPart` cast suppresses the TypeScript error, but at runtime `new Blob([undefined])` creates a Blob whose text content is literally `"undefined"` — rather than sending no body, which is what the original `body` passthrough (`typeof body === 'string' ? ... : body`) effectively did (`sendBeacon(url, undefined)` sends no body per the Beacon spec).
A simple guard avoids the cast entirely and preserves the original no-data behaviour:
```suggestion
const sendBeaconBody = !body ? body : body instanceof Blob ? body : new Blob([body], { type: contentType })
```
Or equivalently add an early return:
```ts
if (!body) return
const sendBeaconBody = body instanceof Blob ? body : new Blob([body], { type: contentType })
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(browser): wrap sendBeacon body in Bl..." | Re-trigger Greptile |
| // 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], { type: contentType }) |
There was a problem hiding this comment.
does not interfer with #3172 cus this is sendBeacon transport only
|
Size Change: +208 B (0%) Total Size: 6.57 MB
ℹ️ View Unchanged
|
dustinbyrne
left a comment
There was a problem hiding this comment.
@dustinbyrne, do you know a way to validate this? no idea how i'd test this locally
Considering it broke, we might not have any meaningful test coverage that actually uses the real sendBeacon method. That'd be the easiest way.
Open to adding something like this as a follow up if we want to get this out now.
Problem
Fixes #3262. Users with reverse proxies, WAFs, or CDNs in production that enforce Content-Type headers were getting "POST object expects Content-Type multipart/form-data" errors on all PostHog API requests sent via
sendBeacon.Since commit 2e46959 (PR #3172, "Send gzipped request body as ArrayBuffer"), gzip-compressed bodies changed from
BlobtoArrayBuffer. While this fixed a WebKit issue withfetch, it inadvertently broke thesendBeacontransport:sendBeacon(url, Blob)→ browser uses the Blob'stypeasContent-Type✅sendBeacon(url, ArrayBuffer)→ browser sends no Content-Type header ❌This explains why the issue only manifests in production (where proxies enforce headers) but not in development.
Changes
packages/browser/src/request.ts: In_sendBeacon(), always wrap the body in aBlobwith the correctcontentType. Previously only string bodies were wrapped; now ArrayBuffer bodies are also wrapped, ensuringContent-Type: text/plainis set for gzip-compressed requests. Also added an early return guard whenbodyis undefined to avoid creating a Blob with literal"undefined"content.packages/browser/src/__tests__/request.test.ts: Updated the sendBeacon gzip test to expect aBlob(withtype: 'text/plain') instead of a rawArrayBuffer.Release info Sub-libraries affected
Libraries affected
Checklist
If releasing new changes
pnpm changesetto generate a changeset file