JuliusBrussee/caveman · error

production requires CAVE_PUBLIC_URL to be an HTTPS origin…

Error message

production requires CAVE_PUBLIC_URL to be an HTTPS origin without credentials, path, query, or fragment

What it means

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.

Solutions

  1. Set CAVE_PUBLIC_URL to a bare HTTPS origin, e.g. https://api.example.com (no path/query/fragment/credentials).
  2. Ensure TLS terminates in front of the service so https is the correct public scheme.
  3. If a path prefix is needed, configure it elsewhere — this variable must stay an origin only.
  4. If the URL fails to parse at all, fix malformed syntax (spaces, missing scheme).

Example fix

// before
export CAVE_PUBLIC_URL="http://api.example.com/api?env=prod"
// error: production requires CAVE_PUBLIC_URL to be an HTTPS origin without credentials, path, query, or fragment

// after
export CAVE_PUBLIC_URL="https://api.example.com"
Defensive patterns

Strategy: validation

Validate before calling

func checkPublicURL() error {
    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, e.g. https://api.example.com")
    }
    return nil
}

Try / catch

if err := env.RefuseProductionDefaults(); err != nil {
    if strings.Contains(err.Error(), "CAVE_PUBLIC_URL") {
        log.Fatalf("bad public URL: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/9e6e15f50f73b42d. Report an issue: GitHub.

Appendix: 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 3ee70a1026)