XTLS/Xray-core · error
unexpected HTTP status code:
Error message
unexpected HTTP status code:
What it means
The remote fetch in FetchHTTPContent succeeded at the transport level but the server answered with a status code other than 200. Xray requires exactly HTTP 200 for remote config content; any 3xx/4xx/5xx (redirects are not followed, since the request is hand-built) is rejected with this error. Unlike the other errors here it has no Base cause — the status code is embedded in the message.
Source
Thrown at main/confloader/external/external.go:87
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, "unix", dialAddr)
},
}
}
resp, err := client.Do(&http.Request{
Method: "GET",
URL: parsedTarget,
Close: true,
})
if err != nil {
return nil, errors.New("failed to dial to ", target).Base(err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, errors.New("unexpected HTTP status code: ", resp.StatusCode)
}
content, err := buf.ReadAllToBytes(resp.Body)
if err != nil {
return nil, errors.New("failed to read HTTP response").Base(err)
}
return content, nil
}
// isRemoteSource reports whether arg should be fetched via HTTP (regular
// network or Unix socket) rather than read from the local filesystem.
// Recognized forms:
//
// - http(s)://... regular HTTP(S)
// - @abstract[:/api] abstract socket (Linux/Android)
// - /abs/path:/api filesystem socket, explicit HTTP path
// - /abs/path filesystem socket detected via os.ModeSocketView on GitHub (pinned to 7d214f8b09)
Solutions
- curl -i the exact URL to see the real status code and body
- Fix the path to point at the actual config resource; use a direct 200-OK endpoint, not a redirect
- If auth is required, put the config behind a plain static endpoint or fetch it yourself and pass the local file
- For object storage/CDNs, disable redirect-style delivery or use the final direct URL
Example fix
# before xray run -config https://cdn.example.com/conf # 301 -> unexpected HTTP status code: 301 # after xray run -config https://cdn.example.com/conf/config.json
Defensive patterns
Strategy: validation
Validate before calling
resp, err := http.Get(target) // pre-check
if err == nil && resp.StatusCode == 200 { /* safe to pass to Xray */ } Try / catch
if err := load(); err != nil {
if strings.Contains(err.Error(), "unexpected HTTP status code: 404") { fixPathAndRetry() }
} Prevention
- Use direct 200-OK static endpoints for remote config
- Avoid redirect-based CDNs or signed-URL setups that 302/403
- Monitor config endpoints with uptime checks
When it happens
Trigger: Fetching a config URL that returns 404 (wrong path), 401/403 (auth required), 500 (server error), or a 301/302 redirect (Xray does not follow redirects because it constructs http.Request directly with Close:true).
Common situations: Config URL path typo on the hosting side, a CDN or object store returning 403 without signed URLs, an API gateway demanding auth, or moving the config file without updating the -config argument.
Related errors
- empty HTTP header value: + key
- invalid URL:
- failed to dial to
- failed to read HTTP response
- failed to get outbound handler with tag: ${tag}
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/d63dac324ddddd57.
Report an issue: GitHub.