Skip to content

[Bug?]: v2 seroval-stream server function responses ship without Content-Type β€” proxies content-sniff text/plainΒ #2295

Description

@NicolasdRa

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version (2.0.0 source)

Current behavior 😯

(Rewritten for SolidStart v2 as requested β€” the original v1 report is in the edit history.)

In v2, seroval-stream server-function responses are sent without a Content-Type header. packages/start/src/fns/handler.ts sets one only in the js mode branch:

h3Event.res.headers.set(BODY_FORMAT_KEY, BodyFormat.Seroval);
if (import.meta.env.SEROVAL_MODE === "js") {
  h3Event.res.headers.set("content-type", "text/javascript");
  return serializeToJSStream(instance, result);
}
return serializeToJSONStream(result);   // <-- no content-type (also in the catch branch)

Reverse proxies built on Go's net/http (kamal-proxy, Traefik-style middlewares, any httputil.ReverseProxy where a handler writes the response) content-sniff bodies that lack a Content-Type and inject one. The seroval chunk stream ;0x000048f1;{...} sniffs as text/plain; charset=utf-8, so clients and intermediaries downstream see a mislabeled response.

SolidStart's own v2 client is not broken by this β€” extractBody dispatches on X-Start-Type first, which is the right design. πŸ‘ But the missing header still has real costs:

  • Anything that is not the Start client sees text/plain β€” logging/observability tooling, caches and CDNs applying content-type rules (e.g. compression or transformation filters keyed on text/*), and browser devtools previews.
  • The response is at the mercy of whatever an intermediary guesses. A different sniffer could guess differently per body prefix, so behavior becomes deployment-dependent.
  • HTTP semantics (RFC 9110 Β§8.3): a sender that knows the content's nature ought to declare it; omitting it is what invites sniffing in the first place.
  • Historical evidence of the risk: in v1 (1.3.2) the same missing header + a Go proxy caused every client-side navigation to hang silently, because that client dispatched on Content-Type before x-serialized. v2's dispatch order fixes the hang, but the root omission carried over.

Expected behavior πŸ€”

The server always sets an explicit Content-Type on seroval streams, in both the success and error paths β€” mirroring what js mode already does with text/javascript. application/octet-stream (or a dedicated type like application/x-seroval-stream) works; anything explicit prevents sniffing. One-line change next to the existing BODY_FORMAT_KEY header set.

Steps to reproduce πŸ•Ή

Steps:

  1. Any SolidStart 2.0.0 app with a "use server" function called from the client.
  2. Deploy behind a minimal Go reverse proxy (httputil.ReverseProxy with any body-touching handler, or kamal-proxy as-is).
  3. Call the server function; inspect the response headers in devtools.
  4. Observe injected content-type: text/plain; charset=utf-8 on the seroval stream (direct hit to the origin shows no Content-Type at all).

Context πŸ”¦

We run SolidStart behind kamal-proxy (Kamal is Rails' default deploy tooling, so this proxy is common). On v1 this exact omission produced a hard-to-debug production incident (silent navigation hang, no console errors); we work around it today with middleware that sets Content-Type: application/octet-stream on /_server responses that lack one. Declaring the type at the source would remove the workaround and make the transport deployment-independent. Happy to send a PR if the approach is acceptable.

Your environment 🌎

System:
  OS: Linux (Docker) / macOS 15 local
Binaries:
  Node: 24.x
  pnpm: 10.x
npmPackages:
  @solidjs/start: 2.0.0 (source-reviewed; production repro observed on 1.3.2 behind the same proxy)
  solid-js: 1.9.x
Proxy: kamal-proxy (Go net/http), TLS terminated at proxy

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Start 2.xtargeting SolidStart 2.x versionsbugSomething isn't workingcontribution welcomewe would appreciate a PRseroval

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions