Skip to content

πŸ› Derive the URL scheme from APP_URL, not APP_ENV - #27

Merged
badmike merged 1 commit into
mainfrom
fix/26-url-scheme-from-app-url
Aug 26, 2026
Merged

πŸ› Derive the URL scheme from APP_URL, not APP_ENV#27
badmike merged 1 commit into
mainfrom
fix/26-url-scheme-from-app-url

Conversation

@badmike

@badmike badmike commented Aug 26, 2026

Copy link
Copy Markdown
Member

A default self-hosted Docker install serves plain HTTP on localhost:8000 but generates every URL as https://, so the admin UI loads its HTML and then fails to fetch a single script or stylesheet. The browser reports that as a CORS error. Reported in #26.

The cause is using APP_ENV as a stand-in for "is this served over TLS". It isn't one: docker-compose.yml pins APP_ENV=production while scripts/install.sh writes APP_URL=http://localhost:8000, and AppServiceProvider forced https on any production environment.

APP_URL already states how the instance is reached, so the scheme comes from there now. Behind a TLS terminating proxy the operator sets an https APP_URL and the forcing still applies β€” including when TRUSTED_PROXIES is unset and X-Forwarded-Proto is therefore not believed.

EnsureRevision had the same defect. It passed the environment as redirect()'s $secure argument, which is an absolute override rather than a hint, so the delivery API's rv redirect went to https on a plain HTTP self-host and to plain http under TLS whenever APP_ENV was not production. It now leaves the argument out and inherits the same decision.

Verified

Built the image from this branch and ran the repo's own compose stack on OrbStack, APP_ENV=production and APP_URL=http://localhost:8123 like the installer writes:

image asset origins in the served page
before 52 Γ— https://localhost:8123
after 52 Γ— http://localhost:8123

Before the fix, curl on one of those asset URLs fails the TLS handshake with no HTTP status. After it, the same asset serves 200 text/javascript, 753 KB. Restarting the fixed image with APP_URL=https://cms.example.com over a plain HTTP hop still yields https for all 52.

Hosted production is unaffected: both ECS task definitions set APP_URL=https://app.b10cks.com.

Two unit tests cover both directions, and both fail against the old code.

Fixes #26

The self-hosted Docker stack sets APP_ENV=production and serves plain HTTP
on localhost, so forcing https on any production environment pointed every
asset URL at a port nothing listens on. A default install rendered its admin
shell fine and then failed to load a single script or stylesheet, which the
browser reports as a CORS error.

APP_URL already states how the instance is reached, so the scheme comes from
there now. Behind a TLS terminating proxy the operator sets an https APP_URL
and the forcing still applies, including when TRUSTED_PROXIES is unset and
X-Forwarded-Proto is therefore not believed.

EnsureRevision had the same defect: it passed the environment as redirect()'s
$secure argument, which is an absolute override rather than a hint, so the
delivery API's rv redirect went to https on a plain HTTP self-host and to
plain http under TLS whenever APP_ENV was not production.

Verified against a locally built image on the repo's own compose stack: all
52 asset URLs on the served page flip from https to http, and an https
APP_URL still yields https over a plain HTTP hop.

Fixes #26

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@badmike
badmike merged commit b44b518 into main Aug 26, 2026
4 checks passed
@badmike
badmike deleted the fix/26-url-scheme-from-app-url branch August 26, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default self-hosted Docker Compose setup generates HTTPS asset URLs while only serving HTTP

1 participant