MHSanaei/3x-ui · warning

URL host is required

Error message

URL host is required

What it means

SanitizeHTTPURL requires a non-empty Host with a parseable hostname. It fires when the scheme is http(s) but url.Parse found no authority component — inputs like 'https:///path' (empty host), 'http://' (nothing at all), or 'https:/path' (single slash: url.Parse treats it as path-only on older Go stdlib shapes).

Source

Thrown at internal/web/service/url_safety.go:29

	"github.com/mhsanaei/3x-ui/v3/internal/util/netsafe"
)

// SanitizeHTTPURL validates and normalizes an http(s) URL without resolving
// DNS. Use SanitizePublicHTTPURL at the point of an outbound request.
func SanitizeHTTPURL(raw string) (string, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return "", nil
	}
	u, err := url.Parse(raw)
	if err != nil {
		return "", err
	}
	if u.Scheme != "http" && u.Scheme != "https" {
		return "", fmt.Errorf("unsupported URL scheme %q", u.Scheme)
	}
	if u.Host == "" || u.Hostname() == "" {
		return "", fmt.Errorf("URL host is required")
	}
	clean := &url.URL{
		Scheme:   u.Scheme,
		Host:     u.Host,
		Path:     u.Path,
		RawPath:  u.RawPath,
		RawQuery: u.RawQuery,
		Fragment: u.Fragment,
	}
	return clean.String(), nil
}

// SanitizePublicHTTPURL validates and normalizes an http(s) URL, then blocks
// private/internal targets unless the caller explicitly allows them.
func SanitizePublicHTTPURL(raw string, allowPrivate bool) (string, error) {
	clean, err := SanitizeHTTPURL(raw)
	if err != nil || clean == "" {
		return clean, err

View on GitHub (pinned to ad32144c42)

Solutions

  1. Require and validate the host field at input time (non-empty, matches a hostname regex) before building the URL.
  2. Fix the concatenation site: check the host variable is non-empty before fmt.Sprintf("https://%s/...", host).
  3. Trim input — leading whitespace can make url.Parse put the whole string into Path.

Example fix

// before
url := fmt.Sprintf("https://%s/ping", host) // host == "" -> "https:///ping"

// after
if strings.TrimSpace(host) == "" {
    return errors.New("host is required")
}
url := fmt.Sprintf("https://%s/ping", host)
Defensive patterns

Strategy: validation

Validate before calling

// Build URLs only from a validated non-empty host
host := strings.TrimSpace(host)
if host == "" {
    return errors.New("host is required")
}
target := "https://" + host + path

Type guard

func hasHost(raw string) bool {
    u, err := url.Parse(strings.TrimSpace(raw))
    return err == nil && u.Hostname() != ""
}

Try / catch

clean, err := service.SanitizeHTTPURL(raw)
if err != nil && strings.Contains(err.Error(), "host is required") {
    // prompt user for the address; do not default to localhost
}

Prevention

When it happens

Trigger: Storing 'http://' + an empty address field; concatenation bugs like "http://" + host where host is empty; user typing 'https:/example.com' (missing slash).

Common situations: Frontend forms submitting before the user filled the address; templating/config code joining scheme and host where the host variable is empty; trailing-colon typos.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/f71c8528402c901d. Report an issue: GitHub.