XTLS/Xray-core · error

"headers" can't contain "host"

Error message

"headers" can't contain "host"

What it means

SplitHTTPConfig.Build() iterates the "headers" map and rejects any key equal (case-insensitively) to "host" at transport_method.go:328-332. The HTTP Host header is governed by the dedicated "host" field (priority on the client: host > serverName > address, per the comment), so duplicating it inside "headers" is ambiguous and forbidden.

Source

Thrown at infra/conf/transport_method.go:330

		}
		extra.Host = c.Host
		extra.Path = c.Path
		extra.Mode = c.Mode
		c = &extra
	}

	switch c.Mode {
	case "":
		c.Mode = "auto"
	case "auto", "packet-up", "stream-up", "stream-one":
	default:
		return nil, errors.New("unsupported mode: " + c.Mode)
	}

	// Priority (client): host > serverName > address
	for k := range c.Headers {
		if strings.ToLower(k) == "host" {
			return nil, errors.New(`"headers" can't contain "host"`)
		}
	}

	if c.XPaddingBytes != (Int32Range{}) && (c.XPaddingBytes.From <= 0 || c.XPaddingBytes.To <= 0) {
		return nil, errors.New("xPaddingBytes cannot be disabled")
	}

	if c.XPaddingKey == "" {
		c.XPaddingKey = "x_padding"
	}

	if c.XPaddingHeader == "" {
		c.XPaddingHeader = "X-Padding"
	}

	switch c.XPaddingPlacement {
	case "":
		c.XPaddingPlacement = "queryInHeader"

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Remove the Host entry from "headers" and use the top-level "host" field instead
  2. Keep all other camouflage headers (User-Agent, Accept, etc.) in "headers" as-is
  3. Search case-insensitively when auditing — "HOST", "Host", "hOsT" all trigger it

Example fix

// before
"host": "dl.example.com", "headers": { "Host": "dl.example.com", "User-Agent": "curl/8" }
// after
"host": "dl.example.com", "headers": { "User-Agent": "curl/8" }
Defensive patterns

Strategy: validation

Validate before calling

// Go: reject Host keys before Build()
for k := range cfg.Headers {
	if strings.EqualFold(k, "host") {
		return errors.New("move Host to the top-level host field")
	}
}

Prevention

When it happens

Trigger: splithttp transportSettings with "headers": {"Host": "example.com"} or {"host": "..."} — any casing passes strings.ToLower. Triggers regardless of mode, before padding checks.

Common situations: Copying a full browser header set (including Host) into headers for camouflage; migrating from websocket/http transport configs where a Host header was the normal way to set SNI/authority; merging example configs that predate this restriction.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/84a914dafc3842f2. Report an issue: GitHub.