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.ModeSocket

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. curl -i the exact URL to see the real status code and body
  2. Fix the path to point at the actual config resource; use a direct 200-OK endpoint, not a redirect
  3. If auth is required, put the config behind a plain static endpoint or fetch it yourself and pass the local file
  4. 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

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


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