{"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":"error","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/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/env/env.go#L99-L129","documentation":"validateProductionPublicURL requires CAVE_PUBLIC_URL to be exactly an HTTPS origin: scheme https, non-empty hostname, no userinfo, no query, no fragment, and path empty or '/'. Any deviation raises this fixed-message error so the public data plane always advertises a clean canonical origin.","triggerScenarios":"RefuseProductionDefaults or RefuseGatewayProductionDefaults runs while CAVE_PUBLIC_URL is http://, missing, has a path like /api, includes credentials (user:pass@), or carries ?query or #fragment.","commonSituations":"Setting the URL to a localhost http address in a prod deploy; including a trailing API path; pasting a full URL with query params from a browser; leaving the variable empty so it parses to nothing.","solutions":["Set CAVE_PUBLIC_URL to a bare HTTPS origin, e.g. https://api.example.com (no path/query/fragment/credentials).","Ensure TLS terminates in front of the service so https is the correct public scheme.","If a path prefix is needed, configure it elsewhere — this variable must stay an origin only.","If the URL fails to parse at all, fix malformed syntax (spaces, missing scheme)."],"exampleFix":"// before\nexport CAVE_PUBLIC_URL=\"http://api.example.com/api?env=prod\"\n// error: production requires CAVE_PUBLIC_URL to be an HTTPS origin without credentials, path, query, or fragment\n\n// after\nexport CAVE_PUBLIC_URL=\"https://api.example.com\"","handlingStrategy":"validation","validationCode":"func checkPublicURL() error {\n    u, err := url.Parse(strings.TrimSpace(os.Getenv(\"CAVE_PUBLIC_URL\")))\n    if err != nil || u.Scheme != \"https\" || u.Hostname() == \"\" || u.User != nil ||\n        u.RawQuery != \"\" || u.Fragment != \"\" || (u.Path != \"\" && u.Path != \"/\") {\n        return errors.New(\"CAVE_PUBLIC_URL must be a bare HTTPS origin, e.g. https://api.example.com\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := env.RefuseProductionDefaults(); err != nil {\n    if strings.Contains(err.Error(), \"CAVE_PUBLIC_URL\") {\n        log.Fatalf(\"bad public URL: %v\", err)\n    }\n    return err\n}","preventionTips":["Store the public origin as scheme + host only, never a full path with query/fragment.","Ensure TLS is provisioned before setting CAVE_PUBLIC_URL to an https origin.","Add a URL lint step to deploy pipelines that mirrors this rule."],"tags":["url","production","https","configuration"],"backgroundTag":"invalid-url-format","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}