[Self-host] Instance incorrectly attempts HTTPS upgrade in spite of environment configuration

(#612) Bug Won't fix self-hosting

Summary

My use case does not permit HTTPS for technical reasons. When configuring a self-hosted Fluxer instance, I had set FLUXER_PUBLIC_SCHEME to http and FLUXER_PUBLIC_PORT to 80 during my environment configuration. Despite this, the stack still appears to force an HTTPS upgrade (and fails).

Steps to reproduce

  1. Following the self-host guide up until step 7 (verify the instance)
  2. Run the curl commands as listed
  3. Observe HTTP 308 (permanent redirect) instead of HTTP 200 (ok) as the response
  4. Tried it in browser anyway and received a security error from a bogus HTTPS redirect

Environment

Fluxer v2 (public) on Docker 29.5.3, Docker Compose 5.1.4, Debian 13 trixie Attempted connections were made using system curl and Firefox 151.0.1

Logs or screenshots

.env (partial)
FLUXER_DOMAIN=bogus.domain.invalid
FLUXER_PUBLIC_SCHEME=http
FLUXER_PUBLIC_PORT=80
FLUXER_CADDY_SITE_ADDRESS=bogus.domain.invalid
# docker compose logs -f api
api-1  | {"level":"warn","time":"2026-06-17T00:47:32.632Z","service":"fluxer-api","env":"production","msg":"Twilio inbound SMS webhook not configured (need integrations.sms.inbound_webhook_auth_token + integrations.sms.inbound_webhook_public_url); skipping installation"}
api-1  | {"level":"info","time":"2026-06-17T00:47:32.638Z","service":"fluxer-api","env":"production","msg":"Initializing API service..."}
api-1  | {"level":"info","time":"2026-06-17T00:47:32.674Z","service":"fluxer-api","env":"production","msg":"Postgres KV client initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:32.678Z","service":"fluxer-api","env":"production","msg":"IP ban cache initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:32.683Z","service":"fluxer-api","env":"production","msg":"Abusive-IP auto-banner replication started"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.245Z","service":"fluxer-api","env":"production","ipv4Count":1253,"ipv6Count":788,"msg":"Tor exit list fetched and hydrated on startup"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.248Z","service":"fluxer-api","env":"production","msg":"Tor exit list cache initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.297Z","service":"fluxer-api","env":"production","msg":"URL blocklist cache initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.299Z","service":"fluxer-api","env":"production","msg":"File SHA blocklist cache initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.301Z","service":"fluxer-api","env":"production","msg":"Phrase blocklist cache initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.304Z","service":"fluxer-api","env":"production","msg":"Banned avatar hash cache initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.307Z","service":"fluxer-api","env":"production","msg":"Profile substring blocklist cache initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.326Z","service":"fluxer-api","env":"production","msg":"LimitConfigService initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.326Z","service":"fluxer-api","env":"production","msg":"Service singletons initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.328Z","service":"fluxer-api","env":"production","count":0,"msg":"RiskCacheManager: loaded disposable domains from DB"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.328Z","service":"fluxer-api","env":"production","source":"startup","disposableDomainCount":0,"msg":"Risk cache initialized on API startup"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.334Z","service":"fluxer-api","env":"production","msg":"JetStream worker service initialized"}
api-1  | {"level":"warn","time":"2026-06-17T00:47:33.335Z","service":"fluxer-api","env":"production","msg":"KV deletion queue needs rebuild, rebuilding..."}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.335Z","service":"fluxer-api","env":"production","msg":"Starting deletion queue rebuild from Cassandra"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.337Z","service":"fluxer-api","env":"production","totalProcessed":0,"totalQueued":0,"msg":"Deletion queue rebuild completed"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.337Z","service":"fluxer-api","env":"production","msg":"Initializing data-service search..."}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.337Z","service":"fluxer-api","env":"production","url":"http://meilisearch:7700","msg":"Using Meilisearch for search"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.640Z","service":"fluxer-api","env":"production","host":"http://meilisearch:7700","msg":"MeilisearchSearchProvider initialised"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.640Z","service":"fluxer-api","env":"production","msg":"Search backend initialized successfully"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.640Z","service":"fluxer-api","env":"production","msg":"Search initialized"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.641Z","service":"fluxer-api","env":"production","msg":"Starting admin search index warmup"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.643Z","service":"fluxer-api","env":"production","msg":"Guild index is empty, populating from database"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.644Z","service":"fluxer-api","env":"production","total":0,"msg":"Guild warmup complete"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.646Z","service":"fluxer-api","env":"production","msg":"User index is empty, populating from database"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.647Z","service":"fluxer-api","env":"production","total":0,"msg":"User warmup complete"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.649Z","service":"fluxer-api","env":"production","msg":"Report index is empty, populating from database"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.649Z","service":"fluxer-api","env":"production","total":0,"msg":"Report warmup complete"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.649Z","service":"fluxer-api","env":"production","msg":"Admin search index warmup complete"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.650Z","service":"fluxer-api","env":"production","msg":"API service initialization complete"}
api-1  | {"level":"info","time":"2026-06-17T00:47:33.651Z","service":"fluxer-api","env":"production","port":8080,"msg":"Starting Fluxer API on port 8080"}
api-1  | {"level":"warn","time":"2026-06-17T00:47:35.791Z","service":"fluxer-api","env":"production","msg":"[ServiceMiddleware] integrations.risk_integration.enabled is false — account risk scoring is disabled"}
api-1  | {"level":"info","time":"2026-06-17T00:47:35.811Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":39,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:48:35.823Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":6,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:49:35.831Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":4,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:50:35.837Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":3,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:51:35.845Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":5,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:52:33.544Z","service":"fluxer-api","env":"production","count":59111,"msg":"RiskCacheManager: loaded disposable domains from DB"}
api-1  | {"level":"info","time":"2026-06-17T00:52:33.544Z","service":"fluxer-api","env":"production","source":"interval","disposableDomainCount":59111,"msg":"Risk cache refresh complete on API"}
api-1  | {"level":"info","time":"2026-06-17T00:52:35.853Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":4,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:53:35.860Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":3,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:54:35.867Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":3,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:55:35.875Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":4,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:56:35.882Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":3,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:57:33.687Z","service":"fluxer-api","env":"production","count":109900,"msg":"RiskCacheManager: loaded disposable domains from DB"}
api-1  | {"level":"info","time":"2026-06-17T00:57:33.687Z","service":"fluxer-api","env":"production","source":"interval","disposableDomainCount":109900,"msg":"Risk cache refresh complete on API"}
api-1  | {"level":"info","time":"2026-06-17T00:57:35.892Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":5,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:58:35.898Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":2,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T00:59:35.904Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":3,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:00:35.909Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":2,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:01:35.915Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":2,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:02:34.049Z","service":"fluxer-api","env":"production","count":156844,"msg":"RiskCacheManager: loaded disposable domains from DB"}
api-1  | {"level":"info","time":"2026-06-17T01:02:34.049Z","service":"fluxer-api","env":"production","source":"interval","disposableDomainCount":156844,"msg":"Risk cache refresh complete on API"}
api-1  | {"level":"info","time":"2026-06-17T01:02:35.924Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":4,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:03:35.932Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":3,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:04:35.942Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":6,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:05:35.950Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":3,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:06:35.961Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":7,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:07:34.285Z","service":"fluxer-api","env":"production","count":202669,"msg":"RiskCacheManager: loaded disposable domains from DB"}
api-1  | {"level":"info","time":"2026-06-17T01:07:34.286Z","service":"fluxer-api","env":"production","source":"interval","disposableDomainCount":202669,"msg":"Risk cache refresh complete on API"}
api-1  | {"level":"info","time":"2026-06-17T01:07:35.971Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":6,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:08:35.978Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":2,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:09:35.987Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":5,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:10:35.995Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":4,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:11:36.004Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":5,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:12:34.468Z","service":"fluxer-api","env":"production","count":250555,"msg":"RiskCacheManager: loaded disposable domains from DB"}
api-1  | {"level":"info","time":"2026-06-17T01:12:34.468Z","service":"fluxer-api","env":"production","source":"interval","disposableDomainCount":250555,"msg":"Risk cache refresh complete on API"}
api-1  | {"level":"info","time":"2026-06-17T01:12:36.016Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":7,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:13:36.022Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":2,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:14:36.030Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":5,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:15:36.037Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":3,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:16:36.045Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":4,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:17:34.249Z","service":"fluxer-api","env":"production","count":276341,"msg":"RiskCacheManager: loaded disposable domains from DB"}
api-1  | {"level":"info","time":"2026-06-17T01:17:34.249Z","service":"fluxer-api","env":"production","source":"interval","disposableDomainCount":276341,"msg":"Risk cache refresh complete on API"}
api-1  | {"level":"info","time":"2026-06-17T01:17:36.054Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":4,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:18:26.659Z","service":"fluxer-api","env":"production","ipv4Count":1256,"ipv6Count":789,"msg":"Tor exit list refreshed"}
api-1  | {"level":"info","time":"2026-06-17T01:18:36.061Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":3,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:19:36.066Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":2,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-06-17T01:20:36.073Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":4,"msg":"Request completed"}
# for path in /_health /api/_health /gateway/_health /media/_health /admin/_health; do curl -fsS -o /tmp/fluxer-check -w "$path %{http_code}\n" "http://$fluxer_domain$path/"; done
/_health 308
/api/_health 308
/gateway/_health 308
/media/_health 308
/admin/_health 308

1 comment

Sign in with Fluxer to comment and vote.
Comment by @doqfgc
RexSystem 1 vote originally by @doqfgc on GitHub OP
Found this is worked around by prepending http:// to {$FLUXER_CADDY_SITE_ADDRESS} at the top of the included Caddyfile. This may not fully work though: I got a webapp crash navigating to any item after logging in, but that's probably not relevant to this issue. Will leave this open in case, though. Update: it is related: crypto.randomUUID is only available on secure endpoints. Thus, unless another large-scale refactor is in order, this is probably never fixable ever. Closing as not planned.