-
-
Notifications
You must be signed in to change notification settings - Fork 476
Description
Summary
The HttpMiddleware.cors function has subtle but important behavior around allowedHeaders that isn't documented in the API or JSDoc comments.
Current Behavior
Looking at the implementation in packages/platform/src/internal/httpMiddleware.ts:
const opts = {
allowedOrigins: ["*"],
allowedMethods: ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"],
allowedHeaders: [], // defaults to empty array
...options
}
const allowHeaders = (
accessControlRequestHeaders: string | undefined
): ReadonlyRecord<string, string> | undefined => {
if (opts.allowedHeaders.length === 0 && accessControlRequestHeaders) {
return {
vary: "Access-Control-Request-Headers",
"access-control-allow-headers": accessControlRequestHeaders // reflects back the request
}
}
if (opts.allowedHeaders) {
return {
"access-control-allow-headers": opts.allowedHeaders.join(",")
}
}
return undefined
}Key insight: When allowedHeaders is an empty array (the default), the middleware reflects back whatever Access-Control-Request-Headers the client sends - effectively allowing all headers. This is a common CORS pattern but not obvious from the API.
The Problem
- The type signature shows
allowedHeaders?: ReadonlyArray<string> | undefinedbut doesn't explain the special behavior of[]vs explicit headers - Users might think an empty array means "no headers allowed" when it actually means "all headers allowed (via reflection)"
- There's no way to express "allow no headers" vs "allow all headers" explicitly
Suggested Documentation
Add JSDoc to clarify:
/**
* @param options.allowedHeaders - Headers to allow in CORS requests.
* - If omitted or empty array: reflects back the client's `Access-Control-Request-Headers` (allows any header)
* - If non-empty array: only allows the specified headers
*/Or consider adding an explicit "*" option for clarity:
allowedHeaders?: ReadonlyArray<string> | "*" | undefinedContext
I discovered this while debugging CORS issues where tracing headers (traceparent, b3, etc.) were being blocked. The fix was either:
- Omit
allowedHeadersentirely (relies on reflection behavior) - Explicitly list all needed headers
Both work, but the reflection behavior wasn't obvious from the API.
🤖 Generated with Claude Code