Describe the bug
Not sure if this should be classified as a bug or just docs improvement, sorry for that!
query.live returns application/x-ndjson, an open response whose body is written incrementally as the server-side async generator yields. SvelteKit sets Cache-Control: private, no-store on the response, which Cloudflare and some other proxies respect for both caching and buffering decisions.
nginx, however, ignores Cache-Control when deciding whether to buffer proxy_pass responses (docs). With its default proxy_buffering on;, nginx holds the entire upstream response until either the body finishes or proxy_read_timeout expires (default 60s). For an open ndjson stream, neither happens cleanly: nginx times out, closes the upstream, and flushes its (typically empty) buffer. The client sees a 200 response complete after ~60s with net::ERR_HTTP2_PROTOCOL_ERROR.
This means any nginx-fronted Node deploy of a SvelteKit app using query.live is broken without the framework or the runtime surfacing an error: live.connected just sits at false and the request hangs in the network panel.
The standard upstream opt-out is the X-Accel-Buffering: no response header. nginx honors it and strips it from the client-facing response. Setting it from the framework on streaming remote-function responses would fix this entire class of deploys without requiring user changes, and is a no-op on proxies that don't recognize the header.
I think it would be nice if this was fixed (by adding the header automatically) or at least maybe better documented!
Reproduction
Minimal repro repo with a live demo: https://github.com/ollema/query-live-repro
Live demo: https://query-live-repro.server.ollema.xyz
Two routes share the same watchCounter (query.live) and bump (command) remote functions and the same in-memory counter:
/broken: no proxy hint, hangs forever behind nginx.
/fixed: X-Accel-Buffering: no set in hooks.server.ts for this route, works as expected.
Steps:
- Open
/broken with devtools open.
- Observe
live.connected === false from first render.
- In the network panel, the
/_app/remote/<hash>/watchCounter request sits pending for ~60s, then "completes" with 200 OK and is immediately logged as net::ERR_HTTP2_PROTOCOL_ERROR 200 (OK).
- Response headers:
Status: 200 OK
cache-control: private, no-store
content-type: application/x-ndjson
server: nginx
- Open
/fixed. Same code, same remote functions, but live.connected === true immediately and bump ticks the counter live. Two /fixed tabs stay in sync.
pnpm dev works in both cases. The bug only appears once nginx (or any other default-buffering reverse proxy) is in front of the Node server. The included Dockerfile and GitHub Actions workflow deploy via CapRover, but any nginx-fronted Node setup reproduces identically.
Logs
System Info
System:
OS: macOS 26.4.1
CPU: (10) arm64 Apple M1 Pro
Memory: 111.22 MB / 32.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 25.9.0 - /opt/homebrew/bin/node
npm: 11.12.1 - /opt/homebrew/bin/npm
pnpm: 10.33.2 - /opt/homebrew/bin/pnpm
Browsers:
Chrome: 146.0.7680.178
Firefox: 125.0.1
Safari: 26.4
npmPackages:
@sveltejs/adapter-node: ^5.5.4 => 5.5.4
@sveltejs/kit: ^2.59.0 => 2.59.0
@sveltejs/vite-plugin-svelte: ^7.0.0 => 7.0.0
svelte: ^5.55.5 => 5.55.5
vite: ^8.0.10 => 8.0.10
Severity
serious, but I can work around it
Additional Information
No response
Describe the bug
Not sure if this should be classified as a bug or just docs improvement, sorry for that!
query.livereturnsapplication/x-ndjson, an open response whose body is written incrementally as the server-side async generator yields. SvelteKit setsCache-Control: private, no-storeon the response, which Cloudflare and some other proxies respect for both caching and buffering decisions.nginx, however, ignores
Cache-Controlwhen deciding whether to bufferproxy_passresponses (docs). With its defaultproxy_buffering on;, nginx holds the entire upstream response until either the body finishes orproxy_read_timeoutexpires (default 60s). For an open ndjson stream, neither happens cleanly: nginx times out, closes the upstream, and flushes its (typically empty) buffer. The client sees a 200 response complete after ~60s withnet::ERR_HTTP2_PROTOCOL_ERROR.This means any nginx-fronted Node deploy of a SvelteKit app using
query.liveis broken without the framework or the runtime surfacing an error:live.connectedjust sits atfalseand the request hangs in the network panel.The standard upstream opt-out is the
X-Accel-Buffering: noresponse header. nginx honors it and strips it from the client-facing response. Setting it from the framework on streaming remote-function responses would fix this entire class of deploys without requiring user changes, and is a no-op on proxies that don't recognize the header.I think it would be nice if this was fixed (by adding the header automatically) or at least maybe better documented!
Reproduction
Minimal repro repo with a live demo: https://github.com/ollema/query-live-repro
Live demo: https://query-live-repro.server.ollema.xyz
Two routes share the same
watchCounter(query.live) andbump(command) remote functions and the same in-memory counter:/broken: no proxy hint, hangs forever behind nginx./fixed:X-Accel-Buffering: noset inhooks.server.tsfor this route, works as expected.Steps:
/brokenwith devtools open.live.connected === falsefrom first render./_app/remote/<hash>/watchCounterrequest sits pending for ~60s, then "completes" with200 OKand is immediately logged asnet::ERR_HTTP2_PROTOCOL_ERROR 200 (OK)./fixed. Same code, same remote functions, butlive.connected === trueimmediately and bump ticks the counter live. Two/fixedtabs stay in sync.pnpm devworks in both cases. The bug only appears once nginx (or any other default-buffering reverse proxy) is in front of the Node server. The includedDockerfileand GitHub Actions workflow deploy via CapRover, but any nginx-fronted Node setup reproduces identically.Logs
System Info
System: OS: macOS 26.4.1 CPU: (10) arm64 Apple M1 Pro Memory: 111.22 MB / 32.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 25.9.0 - /opt/homebrew/bin/node npm: 11.12.1 - /opt/homebrew/bin/npm pnpm: 10.33.2 - /opt/homebrew/bin/pnpm Browsers: Chrome: 146.0.7680.178 Firefox: 125.0.1 Safari: 26.4 npmPackages: @sveltejs/adapter-node: ^5.5.4 => 5.5.4 @sveltejs/kit: ^2.59.0 => 2.59.0 @sveltejs/vite-plugin-svelte: ^7.0.0 => 7.0.0 svelte: ^5.55.5 => 5.55.5 vite: ^8.0.10 => 8.0.10Severity
serious, but I can work around it
Additional Information
No response