【Rails】HTTPヘッダを利用した値の受け渡し方法と注意点

Ruby

Rails上でHTTPヘッダを取得する方法

RailsアプリケーションでHTTPヘッダの情報を取得する場合はrequest.headersあるいはrequest.envを利用します。

### コンソール
$ curl http://localhost:3001/health
### Rails側
From: /app/app/controllers/health_controller.rb:3 HealthController#show:

    2: def show
 => 3:   binding.pry
    4:   render json: { message: 'ok' }, status: :ok
    5: end

# フィールド名が『HTTP_HOST』 の値を取得
## request.headersを利用した場合
[1] pry(<HealthController>)> request.headers["HTTP_HOST"]
=> "localhost:3001"
## request.envを利用した場合
[2] pry(<HealthController>)> request.env["HTTP_HOST"]
=> "localhost:3001"

# 接頭辞が『HTTP_』のHTTPヘッダの一覧取得
## request.headersを利用した場合
[3] pry(<HealthController>)> request.headers.filter_map { |key, value| "#{key}: #{value}" if key.start_with?("HTTP_") }
=> ["HTTP_VERSION: HTTP/1.1", "HTTP_HOST: localhost:3001", "HTTP_USER_AGENT: curl/7.85.0", "HTTP_ACCEPT: */*"]
## request.envを利用した場合
[4] pry(<HealthController>)> request.env.filter_map { |key, value| "#{key}: #{value}" if key.start_with?("HTTP_") }
=> ["HTTP_VERSION: HTTP/1.1", "HTTP_HOST: localhost:3001", "HTTP_USER_AGENT: curl/7.85.0", "HTTP_ACCEPT: */*"]

HTTPヘッダをRailsに渡す際の仕様について

RailsアプリケーションはRack(RubyでWebサーバを立ち上げるためのインタフェース)を利用してデータのやりとりを行っています。
Rackの仕様はRFC 3875 – The Common Gateway Interface (CGI) Version 1.1に準拠しており、4.1.18. Protocol-Specific Meta-Variablesの項目には以下のような記述があります。

  • HTTPヘッダのフィールド名を大文字に変換する
  • 『-』を『_』に置き換える
  • 『HTTP_』を先頭に付ける

つまり、リクエスト時に送信されたHTTPヘッダのフィールド名はRailsアプリケーションに届く過程で暗黙的に変換されます。
ですので、Railsアプリケーション上でHTTPヘッダから値を取得する際は、リクエスト時のフィールド名と異なっているので注意が必要です。

### コンソール
$ curl http://localhost:3001/health -H 'X-CUSTOM-TOKEN: foo'
### Rails側
From: /app/app/controllers/health_controller.rb:3 HealthController#show:

    2: def show
 => 3:   binding.pry
    4:   render json: { message: 'ok' }, status: :ok
    5: end


# request.headersを利用した場合
[1] pry(<HealthController>)> request.headers["HTTP_X_CUSTOM_TOKEN"]
=> "foo"
# request.envを利用した場合
[2] pry(<HealthController>)> request.env["HTTP_X_CUSTOM_TOKEN"]
=> "foo"

# request.headersを利用した場合、リクエスト時のヘッダ名でも取得できる
[3] pry(<HealthController>)> request.headers["X-CUSTOM-TOKEN"]
=> "foo"
# request.envを利用した場合、リクエスト時のヘッダ名では取得できない
[4] pry(<HealthController>)> request.env["X-CUSTOM-TOKEN"]
=> nil

HTTPヘッダに独自のフィールドを追加する場合、名前にはアンダースコアではなくハイフンを使った方が良い

nginxのunderscores_in_headers(アンダースコアを含んだHTTPフィールドの有効可否の設定)の設定がデフォルトでOFFになっていることからもわかる通り1、原則HTTPヘッダのフィールド名にアンダースコアは含めないほうがよいです。
ですので、HTTPヘッダに独自のフィールドを追加する際は名前にはアンダースコアではなくハイフンを利用するようにしましょう。

Rails上でHTTPヘッダが取得できない場合はフィールド名にアンダースコアが含まれているか確認する

たとえば、Railsアプリケーションの前段にunderscores_in_headersがOFFのnginxが配置されいている場合、アンダースコアが含まれたフィールド名のHTTPヘッダはRailsアプリケーションに届きません。
もしHTTPヘッダの情報がRailsアプリケーションに届かない場合はヘッダ名を確認するとよいです。

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考資料