0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ruby on railsで複数ホストにCORSを設定する方法

Last updated at Posted at 2019-03-25

REST API向け、メモ程度です。

環境

  • ruby 2.5.3
  • rails 5.2.2
  • 環境変数 ALLOWED_ORIGINSにカンマ区切りで許可したいホスト名を指定
    • ALLOWED_ORIGINS="localhost,localhost:3000,somehost..."

1. routesでOPTIONSを受け入れるようにする

CORS preflight requestを受け入れられるように routes.rbを編集します。

routes.rb
Rails.application.routes.draw do
  # 中略
  get 'some_api' => 'some_api#some_api'
  # この設定例はあらゆるパスに対するOPTIONSを受け入れるので適宜修正してください
  match '*path', {
      controller: 'some_api',
      action: 'preflight',
      constraints: { method: 'OPTIONS' },
      via: [:options]
  }
end

2. CORS preflight requestに応答できるようにする

some_api_controller.rb
class SomeApiController < ActionController::API
  before_action :ensure_request_from_allowed_origin, only: [:some_api]
  def preflight
    set_preflight_headers!
    head 204
  end

  def some_api
    # CORS対象にしたいAPI
    # 正しいContent-Typeを指定しなければCross-Origin Read Blocking (CORB)でブロックされるので注意
    "some response"
  end

  private
  # CORS対象にしたいAPIのヘッダを設定。filterとしてbefore_actionで指定できるようにする例
  def ensure_request_from_allowed_origin
    origin_host = get_request_host
    return unless origin_host.present?
    head 403 and return unless is_request_from_allowed_origin(origin_host)
    # 許可されたホストからのみAccess-Control-Allow-Originヘッダを付与して応答します
    # 特定METHODのみ許可するならMETHODのチェックもここで行ってください
    response.headers.merge!(
        {
            'Access-Control-Allow-Origin' => request.headers['Origin']
        }
    )
  end

  # リクエストされたホスト名を得る
  def get_request_host
    origin = request.headers['Origin']
    return URI.parse(origin).host if origin.present?
    forwarded_host = request.headers['X-Forwarded-Host']
    return forwarded_host if forwarded_host.present?
    nil
  end

  # CORSが許可されているホストのアクセスからかどうかを調べます。
  # 必要ならスキームのチェックも追加してください。
  # 環境変数 `ALLOWED_ORIGINS`にはカンマ区切りでホスト名を指定してください
  def is_request_from_allowed_origin(origin_host)
    allowed_host_list = ENV['ALLOWED_ORIGINS']
    return false unless origin_host.present? && allowed_host_list.present?
    hosts = allowed_host_list.split(',')
    hosts.include? origin_host
  end

  # プリフライトレスポンスヘッダを設定
  # 許可されていないホストの場合403
  # 許可されているホストの場合、`Access-Control-Allow-Origin`を該当ホストに置き換えて応答します
 def set_preflight_headers!
    origin_host = get_request_host
    return unless origin_host.present?
    head 403 and return unless is_request_from_allowed_origin(origin_host)

    response.headers.merge!(
        {
            'Access-Control-Max-Age' => 600,
            'Access-Control-Allow-Methods' => 'GET', # 対応したい任意のMETHODに置き換えてください
            'Access-Control-Allow-Origin' => request.headers['Origin'],
            'X-Content-Type-Options' => 'nosniff',
            'Vary' => 'Origin' # 複数ホストに許可するために、Originの内容に応じてレスポンスが変化することを明示します。
                               # ただし、Access-Control-Allow-Originに*を指定する場合は不要です。
        }
    )
  end
end
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?