{"record":{"id":"9e6e15f50f73b42d","repo":"JuliusBrussee/caveman","slug":"production-requires-cave-public-url-to-be-an-https","errorCode":null,"errorMessage":"production requires CAVE_PUBLIC_URL to be an HTTPS origin without credentials, path, query, or fragment","messagePattern":"production requires CAVE_PUBLIC_URL to be an HTTPS origin without credentials, path, query, or fragment","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"shared/platform/env/env.go","lineNumber":117,"sourceCode":"\t\tv := strings.TrimSpace(os.Getenv(name))\n\t\tif v == \"\" || v == \"generated\" || strings.Contains(strings.ToLower(v), \"changeme\") {\n\t\t\treturn fmt.Errorf(\"production refuses default or empty %s\", name)\n\t\t}\n\t\tif len(v) < 32 {\n\t\t\treturn fmt.Errorf(\"production requires %s to contain at least 32 characters\", name)\n\t\t}\n\t\tif distinctBytes([]byte(v)) < 8 {\n\t\t\treturn fmt.Errorf(\"production refuses low-diversity %s\", name)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc validateProductionPublicURL() error {\n\tpublicURL := strings.TrimSpace(os.Getenv(\"CAVE_PUBLIC_URL\"))\n\tu, err := url.Parse(publicURL)\n\tif err != nil || u.Scheme != \"https\" || u.Hostname() == \"\" || u.User != nil || u.RawQuery != \"\" || u.Fragment != \"\" || (u.Path != \"\" && u.Path != \"/\") {\n\t\treturn fmt.Errorf(\"production requires CAVE_PUBLIC_URL to be an HTTPS origin without credentials, path, query, or fragment\")\n\t}\n\treturn nil\n}\n\nfunc distinctBytes(value []byte) int {\n\tseen := map[byte]struct{}{}\n\tfor _, b := range value {\n\t\tseen[b] = struct{}{}\n\t}\n\treturn len(seen)\n}\n","sourceCodeStart":99,"sourceCodeEnd":129,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/env/env.go#L99-L129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before\nCAVE_PUBLIC_URL=https://user:pw@portal.example.com/cave/?src=env\n\n# after\nCAVE_PUBLIC_URL=https://portal.example.com","handlingStrategy":"validation","validationCode":"u, err := url.Parse(strings.TrimSpace(os.Getenv(\"CAVE_PUBLIC_URL\")))\nif err != nil || u.Scheme != \"https\" || u.Hostname() == \"\" || u.User != nil || u.RawQuery != \"\" || u.Fragment != \"\" || (u.Path != \"\" && u.Path != \"/\") {\n    return errors.New(\"CAVE_PUBLIC_URL must be a bare HTTPS origin\")\n}","typeGuard":null,"tryCatchPattern":"if err := env.RefuseProductionDefaults(); err != nil {\n    log.Fatalf(\"config: %v\", err)\n}","preventionTips":["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."],"tags":["go","production","config","url","security"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}