jnunemaker/httparty · error · HTTParty::DuplicateLocationHeader

#{response}

Error message

#{response}

What it means

HTTParty raises HTTParty::DuplicateLocationHeader when it tries to follow a redirect and the response contains more than one Location header (last_response.get_fields('location') returns an array with count > 1). Duplicate Location headers make the redirect target ambiguous and are a classic sign of response splitting or a broken proxy, so HTTParty refuses to guess which one to follow. The exception inherits from HTTParty::ResponseError, so e.response holds the Net::HTTPResponse that triggered it; the message is just the response object rendered as a string, which is why it looks like "#{response}".

Source

Thrown at lib/httparty/request.rb:366

      end
      if http_method == Net::HTTP::Get
        clear_body
      end
      capture_cookies(last_response)
      perform(&block)
    end

    def handle_host_redirection
      check_duplicate_location_header
      redirect_path = options[:uri_adapter].parse(last_response['location']).normalize
      return if redirect_path.relative? || path.host == redirect_path.host || uri.host == redirect_path.host
      @changed_hosts = true
    end

    def check_duplicate_location_header
      location = last_response.get_fields('location')
      if location.is_a?(Array) && location.count > 1
        raise DuplicateLocationHeader.new(last_response)
      end
    end

    def send_authorization_header?
      !@changed_hosts
    end

    def response_redirects?
      case last_response
      when Net::HTTPNotModified # 304
        false
      when Net::HTTPRedirection
        options[:follow_redirects] && last_response.key?('location')
      end
    end

    def parse_response(body)
      parser.call(body, format)

View on GitHub (pinned to 8f4a09e343)

Solutions

  1. Inspect e.response.headers.get_fields('location') in the rescue block to confirm the duplicate and see both values.
  2. Fix the origin: ensure only one Location header is emitted on redirect responses (check proxy add_header/append and backend framework redirect helpers).
  3. If the error comes from a test, fix the stub: WebMock `to_redirect('http://x/')` instead of hand-writing headers with multiple Location entries.
  4. As a last resort, rescue HTTParty::DuplicateLocationHeader and treat it as a protocol error (retry without redirects via follow_redirects(false) and inspect manually).

Example fix

# before (server/stub sends two Location headers)
stub_request(:get, 'http://api.example.com/old')
  .to_return(status: 302, headers: { 'Location' => ['/a', '/b'] })
Foo.get('http://api.example.com/old')
# => HTTParty::DuplicateLocationHeader

# after (exactly one Location header)
stub_request(:get, 'http://api.example.com/old')
  .to_return(status: 302, headers: { 'Location' => '/a' })
Foo.get('http://api.example.com/old')
Defensive patterns

Strategy: try-catch

Try / catch

begin
  Foo.get('http://api.example.com/old')
rescue HTTParty::DuplicateLocationHeader => e
  locations = e.response.headers.get_fields('location')
  Rails.logger.error("ambiguous redirect, #{locations.size} Location headers: #{locations.inspect}")
  raise
end

Prevention

When it happens

Trigger: Any request with follow_redirects enabled (the default) where the server answers 301/302/303/307/308 with two or more Location headers. check_duplicate_location_header runs inside handle_host_redirection before the redirect is parsed, e.g. `Foo.get('http://api.example.com/old')` against a host whose 302 response repeats Location twice.

Common situations: WebMock/VCR stubs that declare the Location header twice (once as a scalar and once in a with-headers hash), misconfigured nginx/HAProxy setups where both the backend and the proxy add Location, CDN edge rewrites that stack on top of origin redirects, and actual HTTP response-splitting from a vulnerable backend.

Related errors


AI-assisted analysis of jnunemaker/httparty@8f4a09e343 (2026-08-21). Data as JSON: /api/errors/24dc9cdcb3979f36. Report an issue: GitHub.