JuliusBrussee/caveman · error

ssrf: scheme %q not permitted (managed mode requires https)

Error message

ssrf: scheme %q not permitted (managed mode requires https)

What it means

ssrf.ValidateURL only permits https URLs; plain http is tolerated solely when Config.ManagedMode is false (self-hosted deployments). Any other scheme (http in managed mode, ftp, ws, file, ...) is rejected before any DNS or network work. Errors are safe to return to callers and deliberately exclude credential material.

Source

Thrown at shared/platform/ssrf/ssrf.go:167

func SelfHostedConfig(allowList ...string) Config {
	return Config{ManagedMode: false, AllowList: allowList}
}

// ValidateURL resolves raw to a URL, validates the scheme/port constraints,
// and checks every IP the hostname resolves to against the SSRF block lists.
// It is a pre-flight check only — see NewDialContext for dial-time enforcement.
//
// Errors are safe to return to callers; they contain the blocked IP but never
// the original credential material.
func ValidateURL(ctx context.Context, raw string, cfg Config) error {
	u, err := url.Parse(raw)
	if err != nil {
		// net/url.Error includes the raw URL (and may therefore include
		// credentials or query secrets). Keep this error field-only and stable.
		return errors.New("ssrf: invalid URL")
	}
	if u.Scheme != "https" && !(u.Scheme == "http" && !cfg.ManagedMode) {
		return fmt.Errorf("ssrf: scheme %q not permitted (managed mode requires https)", u.Scheme)
	}
	if u.User != nil {
		return fmt.Errorf("ssrf: credentials embedded in URL are forbidden")
	}
	host := u.Hostname()
	if host == "" {
		return fmt.Errorf("ssrf: URL must contain a host")
	}
	port := u.Port()
	if cfg.ManagedMode && port != "" && port != "443" {
		return errors.New("ssrf: managed mode requires port 443")
	}
	if port == "" {
		if u.Scheme == "https" {
			port = "443"
		} else {
			port = "80"
		}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Change the endpoint URL to https (most providers support it).
  2. If this is a self-hosted deployment that genuinely needs http, ensure ManagedMode is false in the SSRF Config passed to ValidateURL.
  3. Reject the URL at ingestion (form/API validation) with a clear message so users fix it before runtime.

Example fix

// before
url := "http://api.internal.local:8080/hook"
if err := ssrf.ValidateURL(ctx, url, cfg); err != nil { ... } // blocked in managed mode

// after
url := "https://api.internal.local:8443/hook"
if err := ssrf.ValidateURL(ctx, url, cfg); err != nil { ... }
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(raw)
if err == nil && u.Scheme != "https" && !(u.Scheme == "http" && !cfg.ManagedMode) {
    return fmt.Errorf("use an https URL")
}

Type guard

func isPermittedScheme(raw string, managed bool) bool {
    u, err := url.Parse(raw)
    if err != nil { return false }
    return u.Scheme == "https" || (u.Scheme == "http" && !managed)
}

Try / catch

if err := ssrf.ValidateURL(ctx, raw, cfg); err != nil {
    if strings.Contains(err.Error(), "scheme") {
        // ask the user for the https variant of the endpoint
    }
}

Prevention

When it happens

Trigger: Calling ssrf.ValidateURL with an http:// URL while cfg.ManagedMode is true; or with any non-https/non-http scheme such as ftp://, ws://, file:// in either mode.

Common situations: A user-supplied webhook or provider endpoint configured as http:// in the managed/SaaS environment; local dev URLs (http://localhost:8000) accidentally shipped to a managed deployment; typos like 'httpss://'.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/cc09d98e2a9d5174. Report an issue: GitHub.