[Bug]: Packaged web assets are served uncompressed and without cache headers, causing slow remote/mobile startup
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- Start packaged T3 Code with network access enabled.
- Open the environment from a remote browser with a cold cache.
- Inspect the response for the primary hashed JavaScript asset under
/assets/. - Repeat the request with
Accept-Encoding: gzip, deflate. - Inspect the response's compression and caching headers.
Expected behavior
Packaged web assets should be efficient to load over ordinary remote and mobile connections:
- Hashed assets such as
/assets/index-<hash>.jsshould be served using Brotli or gzip when supported. - Hashed assets should use a long-lived immutable cache policy.
index.htmland SPA fallback responses should revalidate so a new Nightly build is discovered promptly.- Responses should include
Vary: Accept-Encodingwhen content negotiation is used.
One possible policy:
- Hashed
/assets/*:Cache-Control: public, max-age=31536000, immutable index.htmland SPA fallbacks:Cache-Control: no-cache- Prefer precompressed
.br/.gzbuild artifacts over dynamically recompressing the same large bundle for every request.
Actual behavior
Tested with:
- T3 Code Nightly
0.0.29-nightly.20260723.887 - Packaged macOS host
- Android 16 / Firefox 152 remote client
The primary JavaScript response was:
- Uncompressed size:
5,052,639bytes - Size after ordinary gzip:
1,615,396bytes - Remote request duration observed in the T3 server trace:
17.922s - Loopback request duration: approximately
0.004s Accept-Encoding: gzip, deflatedid not change the response size- No
Content-Encoding - No
Cache-Control - No
ETag - No
Last-Modified
The browser therefore has to transfer approximately 5 MB before the SPA can initialize, and cannot reliably reuse or revalidate that asset across launches.
The current static route reads the file and returns a uint8Array with its content type, but does not set compression or caching headers:
https://github.com/pingdotgg/t3code/blob/main/apps/server/src/http.ts
Impact
Major degradation or frequent failure
On constrained remote links, the initial JavaScript transfer alone can leave the page blank for many seconds. Reconnects or hard reloads are especially expensive.
Version or commit
0.0.29-nightly.20260723.887
Environment
macOS host; Android 16; Firefox 152
Logs or stack traces
HTTP/1.1 200 OK
content-type: text/javascript
content-length: 5052639
# No Content-Encoding, Cache-Control, ETag, or Last-Modified
uncompressed_bytes=5052639 local_seconds=0.003692
accept_encoding_gzip_bytes=5052639 local_seconds=0.001826
gzip_of_same_content_bytes=1615396
remote_server_span_duration_seconds=17.922
Suggested acceptance criteria
- A request advertising Brotli/gzip receives a compressed representation.
- Hashed assets receive an immutable cache policy.
index.htmland SPA fallback responses do not become permanently stale after an update.- A warm reload does not transfer the complete hashed bundle again.
- Server tests cover compression negotiation and the different cache policies.
- WebSocket behavior remains unaffected.
Related but separate issues
- #3054 tracks WebSocket heartbeat/reconnect tolerance.
- #2761 tracks oversized thread snapshots and WebSocket backpressure.
Those issues can make recovery more expensive, but the static application bundle is a separate payload delivered before thread hydration begins.
Workaround
Keep the browser tab alive and avoid hard refreshes. A reverse proxy can add compression and caching, but packaged T3 Code should provide sensible defaults directly.
Contribution
Happy to validate a proposed fix against this environment and provide additional sanitized header or timing measurements.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in apps/server/src/http.ts at the static asset route and inspect how packaged files are returned and how SPA fallbacks are handled. Add server tests for compression negotiation and separate cache policies, then verify hashed assets are compressed and immutable while index.html and fallbacks revalidate without affecting WebSocket behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, performance, web-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 70/100