JuliusBrussee/caveman · critical
production requires CAVE_PUBLIC_URL to be an HTTPS origin wi
Error message
production requires CAVE_PUBLIC_URL to be an HTTPS origin without credentials, path, query, or fragment
What it means
Produced by validateProductionPublicURL (env.go:117): in production, CAVE_PUBLIC_URL must parse as an HTTPS origin and nothing more - scheme exactly https, a non-empty hostname, and no userinfo, query, fragment, or path (an optional lone '/' path is tolerated). The public URL is the base for links and browser-facing redirects, so credentials or extra components in it are a configuration defect.
Source
Thrown at shared/platform/env/env.go:117
v := strings.TrimSpace(os.Getenv(name))
if v == "" || v == "generated" || strings.Contains(strings.ToLower(v), "changeme") {
return fmt.Errorf("production refuses default or empty %s", name)
}
if len(v) < 32 {
return fmt.Errorf("production requires %s to contain at least 32 characters", name)
}
if distinctBytes([]byte(v)) < 8 {
return fmt.Errorf("production refuses low-diversity %s", name)
}
}
return nil
}
func validateProductionPublicURL() error {
publicURL := strings.TrimSpace(os.Getenv("CAVE_PUBLIC_URL"))
u, err := url.Parse(publicURL)
if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || u.RawQuery != "" || u.Fragment != "" || (u.Path != "" && u.Path != "/") {
return fmt.Errorf("production requires CAVE_PUBLIC_URL to be an HTTPS origin without credentials, path, query, or fragment")
}
return nil
}
func distinctBytes(value []byte) int {
seen := map[byte]struct{}{}
for _, b := range value {
seen[b] = struct{}{}
}
return len(seen)
}
View on GitHub (pinned to 27d5a3981a)
Solutions
- Set CAVE_PUBLIC_URL to a bare HTTPS origin: https://cave.example.com (trailing '/' allowed).
- Strip userinfo, path prefixes, query, and fragment - route prefixes belong in the proxy, not in this variable.
- If the service must live under a subpath, that is not supported by this gate: serve it on its own (sub)domain instead.
- Verify with a URL parse before deploying (see validation snippet).
Example fix
# before CAVE_PUBLIC_URL=https://user:pw@portal.example.com/cave/?src=env # after CAVE_PUBLIC_URL=https://portal.example.com
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(strings.TrimSpace(os.Getenv("CAVE_PUBLIC_URL")))
if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || u.RawQuery != "" || u.Fragment != "" || (u.Path != "" && u.Path != "/") {
return errors.New("CAVE_PUBLIC_URL must be a bare HTTPS origin")
} Try / catch
if err := env.RefuseProductionDefaults(); err != nil {
log.Fatalf("config: %v", err)
} Prevention
- Set CAVE_PUBLIC_URL once from the site domain only: https://<domain>[/].
- Keep path prefixes in proxy routing, never in this variable.
- Test the URL with url.Parse in deploy scripts before rollout.
When it happens
Trigger: CAVE_ENV is production and CAVE_PUBLIC_URL is e.g. 'http://cave.example.com' (wrong scheme), 'https://user:pw@host' (credentials), 'https://host/base/path' (path), 'https://host/?x=1' (query), 'https://host#f' (fragment), empty, or unparseable.
Common situations: Setting the URL from an ingress definition that includes a path prefix; copying a connection string with embedded basic auth; leaving the dev http:// value when promoting; a proxy that appends query params into the configured origin.
Related errors
- production requires an https:// CLICKHOUSE_URL (TLS only); C
- production KMS configuration: %w
- production refuses default or empty %s
- production requires %s to contain at least 32 characters
- production refuses low-diversity %s
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/9e6e15f50f73b42d.
Report an issue: GitHub.