The title is pretty self-explanatory. Is there any way to get the headers (except for Rack::Request.env[]
)?

- 158,662
- 42
- 215
- 303

- 2,082
- 3
- 17
- 28
3 Answers
The HTTP headers are available in the Rack environment passed to your app:
HTTP_
Variables: Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with HTTP_). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request.
So the HTTP headers are prefixed with "HTTP_" and added to the hash.
Here's a little program that extracts and displays them:
require 'rack'
app = Proc.new do |env|
headers = env.select {|k,v| k.start_with? 'HTTP_'}
.collect {|key, val| [key.sub(/^HTTP_/, ''), val]}
.collect {|key, val| "#{key}: #{val}<br>"}
.sort
[200, {'Content-Type' => 'text/html'}, headers]
end
Rack::Server.start :app => app, :Port => 8080
When I run this, in addition to the HTTP headers as shown by Chrome or Firefox, there is a "VERSION: HTPP/1.1" (i.e. an entry with key "HTTP_VERSION" and value "HTTP/1.1" is being added to the env hash).

- 78,533
- 8
- 163
- 197
-
3Ah, so it's basically `env` anyway :). What I dislike are the upcased names with some chars replaced. Well, I guess I will have to get away with it.... – PJK Jun 11 '11 at 21:34
-
@PJK well the names should be case insensitive anyway: http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2. What characters are being replaced? Are you trying to use characters from outside the ASCII chracter set? Header names should be ASCII only. – matt Jun 11 '11 at 22:16
-
2I know, it is just a matter of convenience... For instance, X-Build becomes HTTP_X_BUILD, which means X_Build and X-BUILD should be equivalent but (I've been told that) browsers differentiate between these two alternatives. – PJK Jun 11 '11 at 22:29
-
3@PJK I see: `-` is being changed to `_`. I guess that's to remain compatible with CGI (an environment variable can't contain `-`). But the _response_ headers shouldn't be affected. – matt Jun 11 '11 at 23:47
-
Link to Rack environment documentation that's not broken: https://github.com/rack/rack/blob/master/SPEC.rdoc#the-environment- – salomvary Jan 14 '21 at 11:36
Based on @matt's answer, but this really gives you the request headers in a hash as requested in the question:
headers = Hash[*env.select {|k,v| k.start_with? 'HTTP_'}
.collect {|k,v| [k.sub(/^HTTP_/, ''), v]}
.collect {|k,v| [k.split('_').collect(&:capitalize).join('-'), v]}
.sort
.flatten]
Depending on what key convention you prefer you might want to use something else instead of :capitalize.

- 18,880
- 12
- 68
- 105
Like @Gavriel's answer, but using transform_keys
(simpler):
class Request
def headers
env.select { |k,v| k.start_with? 'HTTP_'}.
transform_keys { |k| k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-') }
end
end
You can even make it so lookups still work even if the case is different:
def headers
env.
select { |k,v| k.start_with? 'HTTP_'}.
transform_keys { |k| k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-') }.
sort.to_h.
tap do |headers|
headers.define_singleton_method :[] do |k|
super(k.split(/[-_]/).map(&:capitalize).join('-'))
end
end
end
So for example, even if headers
normalizes the keys so it returns this:
{
Dnt: '1',
Etag: 'W/"ec4454af5ae1bacff1afc5a06a2133f4"',
'X-Xss-Protection': '1; mode=block',
}
you can still look up headers using the more natural/common names for these headers:
headers['DNT']
headers['ETag']
headers['X-XSS-Protection']

- 9,191
- 6
- 60
- 60
-
in your example, shouldn't it be `'Dnt': '1'` instead of `Dnt: '1'`? – Ron Klein May 21 '19 at 06:13
-
-
@MarlinPierce Well it is syntactically correct Ruby but it's not what the code produces. @RonKlein is right, it should be `'Dnt': '1',` The keys here are strings from start to end. Capitalized literals denote constants in Ruby. – Arnaud Meuret Nov 05 '19 at 10:42
-
@ArnaudMeuret Now, I think you are mixing up Constants and Literals. In ruby, `'Dnt':` resolves to the symbol :Dnt. If you want string keys, you need `{ 'Dnt' => '1' }`. This will show you that `'Dnt':` is a symbol, `{ 'Dnt': '1' }.each_pair { |key, value| puts key.inspect }`. – Marlin Pierce Nov 06 '19 at 18:49
-
@ArnaudMeuret another way to see this, is that `Dnt='Knock';{ 'Dnt': 1, 'Dnt' => 2, Dnt => 3 }` evaluates to `{:Dnt=>1, "Dnt"=>2, "Knock"=>3}`. – Marlin Pierce Nov 06 '19 at 18:56
-
@MarlinPierce Mmm I'm not. But, with the help of the faulty syntax highlighter, I did overlook that Tyler used the : mapping and misstook it for =>. My point remains that I don't see where his function produces these symbol keys, which you can not look up using strings. – Arnaud Meuret Nov 08 '19 at 03:34
-
now that Rack is adopting all-downcase headers, I believe it would be a good idea to remove the "capitalize" from your code – Everton J. Carpes Sep 23 '22 at 14:59