pingdotgg / pingdotgg/t3code

[Bug]: Packaged web assets are served uncompressed and without cache headers, causing slow remote/mobile startup

Open
#4,398 0 comments 0 reactions 0 assignees View on GitHub

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
  1. Start packaged T3 Code with network access enabled.
  2. Open the environment from a remote browser with a cold cache.
  3. Inspect the response for the primary hashed JavaScript asset under /assets/.
  4. Repeat the request with Accept-Encoding: gzip, deflate.
  5. 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>.js should be served using Brotli or gzip when supported.
  • Hashed assets should use a long-lived immutable cache policy.
  • index.html and SPA fallback responses should revalidate so a new Nightly build is discovered promptly.
  • Responses should include Vary: Accept-Encoding when content negotiation is used.

One possible policy:

  • Hashed /assets/*: Cache-Control: public, max-age=31536000, immutable
  • index.html and SPA fallbacks: Cache-Control: no-cache
  • Prefer precompressed .br/.gz build 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,639 bytes
  • Size after ordinary gzip: 1,615,396 bytes
  • Remote request duration observed in the T3 server trace: 17.922s
  • Loopback request duration: approximately 0.004s
  • Accept-Encoding: gzip, deflate did 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.html and 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.