1 min read

HTTP headers in Rails

Syed Aslam

Accessing the headers sent by clients in rails spits out both headers along with other environment variables. This adds a bit of inconvenience if you want to process only the external headers.

Accessing the headers in Rails is simply request.headers["HEADER_NAME"]. If there are a few of these headers that you want to process and work with then you'll have to enumerate and extract out the headers you want. This is not as straightforward simply because request.headers also include namespaced rails environment variables.

One way you could filter out the required headers is by a brute-force method, selecting all headers matching a certain pattern.

request.env.select { |k,v|
  k.match("^HTTP.*|^CONTENT.*|^REMOTE.*|^REQUEST.*|^AUTHORIZATION.*|^SCRIPT.*|^SERVER.*")
}

If you notice all rails environment variables are namespaced, e.g., action_dispatch.show_exceptions, rack.input etc. By convention, headers do not usually contain dots. Nginx even rejects requests with dots in headers by default.

Having dot in the header name

Basically nginx does this because:

  1. they aren't generally used (http itself only uses alphanumeric and "-" in headers);
  1. they are likely to cause issues, including security ones, with translations like CGI does (X-Blah -> HTTPX_BLAH, X_Blah -> HTTP_X_BLAH, X.Blah -> HTTP_X_BLAH); note that nginx itself uses similar translation for $http* variables and AFAIR while passing headers to fastcgi backends.

So I think it's quite a safe assumption to go with to distinguish external headers from internal variables and reject all keys that have a '.' in them:

request.headers.env.reject { |key| key.to_s.include?('.') }

Works neatly.