encode/httpx · error · RemoteProtocolError

Invalid URL in location header: {exc}.

Error message

Invalid URL in location header: {exc}.

What it means

Raised as httpx.RemoteProtocolError while following a redirect when the response's 'Location' header cannot be parsed by httpx.URL (which raises InvalidURL). _redirect_url wraps that as a remote protocol violation because the server returned an unusable redirect target.

Source

Thrown at httpx/_client.py:526

            method = "GET"

        # If a POST is responded to with a 301, turn it into a GET.
        # This bizarre behaviour is explained in 'requests' issue 1704.
        if response.status_code == codes.MOVED_PERMANENTLY and method == "POST":
            method = "GET"

        return method

    def _redirect_url(self, request: Request, response: Response) -> URL:
        """
        Return the URL for the redirect to follow.
        """
        location = response.headers["Location"]

        try:
            url = URL(location)
        except InvalidURL as exc:
            raise RemoteProtocolError(
                f"Invalid URL in location header: {exc}.", request=request
            ) from None

        # Handle malformed 'Location' headers that are "absolute" form, have no host.
        # See: https://github.com/encode/httpx/issues/771
        if url.scheme and not url.host:
            url = url.copy_with(host=request.url.host)

        # Facilitate relative 'Location' headers, as allowed by RFC 7231.
        # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
        if url.is_relative_url:
            url = request.url.join(url)

        # Attach previous fragment if needed (RFC 7231 7.1.2)
        if request.url.fragment and not url.fragment:
            url = url.copy_with(fragment=request.url.fragment)

        return url

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Send the request with follow_redirects=False and inspect response.headers['Location'] manually.
  2. Fix the server to emit a valid, absolute or proper relative Location URL.
  3. Catch httpx.RemoteProtocolError and retry against a known-good URL.

Example fix

// before
client.get(url, follow_redirects=True)
// after
r = client.get(url, follow_redirects=False)
location = r.headers.get("Location")
# validate/sanitize before re-requesting
Defensive patterns

Strategy: validation

Validate before calling

import httpx
probe = client.get(url, follow_redirects=False)
if probe.is_redirect:
    loc = probe.headers.get("Location", "")
    try:
        httpx.URL(loc)
    except httpx.InvalidURL:
        loc = None  # malformed; do not auto-follow
# only follow when loc is parseable

Type guard

import httpx

def location_is_valid(response: httpx.Response) -> bool:
    if not response.is_redirect:
        return True
    try:
        httpx.URL(response.headers["Location"])
        return True
    except (KeyError, httpx.InvalidURL):
        return False

Try / catch

try:
    resp = client.get(url, follow_redirects=True)
except httpx.RemoteProtocolError as exc:
    if "Invalid URL in location header" in str(exc):
        # server returned a malformed redirect; fetch without following
        resp = client.get(url, follow_redirects=False)
    else:
        raise

Prevention

When it happens

Trigger: A 3xx response with follow_redirects=True whose Location header is malformed (e.g. contains illegal/control characters, an unparseable string), so URL(location) raises InvalidURL inside _redirect_url.

Common situations: Buggy servers/proxies emitting broken Location headers; header injection attempts; misconfigured load balancers returning garbage redirect targets.

Related errors


AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04). Data as JSON: /data/errors/f9c7760d26586aa9.json. Report an issue: GitHub.