Skip to content

Putting Caddy in front of t3 code with https fails due to the web socket not being encrypted #328

@allquixotic

Description

@allquixotic

NOTE: Most of this report is generated by gpt-5.4, but I reviewed it manually and it does refer to a genuine bug I experienced. Also see below why I did not submit a PR.

Problem

T3 Code 0.0.3 could render successfully over HTTPS and still fail to become usable because the frontend attempted to open its control WebSocket over insecure ws:// instead of secure wss://.

This is a product bug in T3's browser client bootstrap logic. The app was assuming a non-TLS websocket origin even when the page itself was loaded from an HTTPS origin behind a reverse proxy.

Symptom

The browser console shows a startup failure similar to:

SecurityError: An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.

In practical terms, the page shell can load, but real-time state hydration and interactive behavior do not come up correctly because the main websocket transport never connects.

Reproduce Conditions

This bug is most likely to appear under all of the following conditions:

  1. T3 is deployed behind a TLS terminator or reverse proxy.
  2. The browser reaches T3 through https://....
  3. T3 itself still listens on plain HTTP internally.
  4. The frontend computes its websocket endpoint from assumptions that ignore the current page protocol.

This is especially easy to trigger in the common production topology:

  • browser -> https://<some-host> (in my case, on Tailscale)
  • reverse proxy terminates TLS
  • proxy forwards to T3 on http://127.0.0.1:<port>

In that topology, ws:// is invalid from the browser's point of view even if the reverse proxy could technically reach the backend over plain HTTP.

Why This Is Broken

The browser's security model requires websocket security to match the page security context:

  • http:// pages may open ws://
  • https:// pages must open wss://

If the frontend derives its websocket URL from a static default, a plain-host assumption, or duplicated URL-building logic that forgets to switch protocols under HTTPS, the app fails only in secured deployments. That makes the defect easy to miss in local development and easy to hit in real deployment.

Internal Fix We Wrote

We patched T3's frontend websocket URL derivation so the default websocket origin is computed from the active browser location:

  • use wss:// when window.location.protocol === "https:"
  • use ws:// otherwise

We also characterized and fixed a second consistency problem at the same time: HTTP-origin derivation for related frontend features was spread across multiple call sites. The internal patch centralized that origin-resolution logic so websocket and HTTP URL derivation use the same source of truth.

Affected source files in the internal fix:

  • /home/<USER>/t3code/apps/web/src/wsUrl.ts
  • /home/<USER>/t3code/apps/web/src/wsTransport.ts
  • /home/<USER>/t3code/apps/web/src/store.ts
  • /home/<USER>/t3code/apps/web/src/components/Sidebar.tsx

Me the user, here (manual edit to this report): However, since T3 Code is not accepting merge requests from external at this time, I am simply reporting that I managed to fix this in my own local copy of the app, and will not be contributing my exact fix unless it is asked of me by someone on the T3 team.

Expected Behavior After Fix

When the app is opened through HTTPS:

  • the websocket target resolves to wss://<current-host>
  • the client connects successfully through the reverse proxy
  • the page no longer fails during startup with a browser security error

When the app is opened through plain HTTP in development:

  • the websocket target still resolves to ws://...
  • local non-TLS development behavior remains intact

Scope of the Issue

This is not a deployment-only mistake. The deployment topology exposed it, but the actual defect was in T3's frontend runtime behavior:

  • protocol-sensitive URL derivation was incorrect or incomplete
  • production-safe websocket bootstrapping was not robust

Any T3 deployment that serves the app through HTTPS without explicitly overriding the websocket URL could encounter the same failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions