6
6

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.

リバースプロキシ経由でSinatraアプリケーションを動かす

Posted at

動機

  • Sinatraアプリケーションを動かしたい。
  • リバースプロキシを経由したい(URL,SSL,パフォーマンス, etc.)
  • URLはルートではなく,サブディレクトリに配置したい

今回のサンプルでは,リバースプロキシとしてApacheを使っているが,nginxなど,他のWebサーバでも同様のはず。

準備

サンプルアプリケーション

application.rb
require "sinatra"

get "/" do
  "Hello!"
end

get "/redirect" do
  redirect to("dest")
end

get "/dest" do
  "redirected"
end

これを

$ ruby application.rb

とすることで, http://localhost:4567/ でサンプルアプリケーションが起動する。

リバースプロキシの設定 (Apache 2.4)

上で用意したサンプルアプリケーションを,
http://example.com/example-app/
として動作させたい。
※SinatraアプリケーションとApacheは同じホスト(example.com)上で動作しているとする。

apacheの設定ファイルで,

ProxyPass /example-app/ http://localhost:4567/
ProxyPassReverse /example-app/ http://localhost:4567/

これで,Webブラウザから http://example.com/example-app/ にアクセスすると,画面に「Hello!」という文字列が表示されるはず。

何が問題?

http://example.com/example-app/redirect にアクセスしてみよう。
本当は, http://example.com/example-app/dest にリダイレクトされて,画面に「redirected」と表示されて欲しい。

が,試すと http://example.com/dest にリダイレクトされ,画面には「404 Not Found」エラーが…。

原因は?

まず,前提条件として,HTTPによるリダイレクトでは,リダイレクト先はアクセス可能なURIで返さねばならず,相対パスで指定することはできない。
上記のサンプルアプリケーションで

application.rb
get "/redirect" do
  redirect to("dest")
end

でリダイレクト処理が書かれている。ここではリダイレクト先が相対指定されているが,Sinatraのtoヘルパメソッド(実際にはuriヘルパメソッドのエイリアス)によって,正しいURIに書き換えられてクライアントに返される。
この時,toヘルパメソッドの中では,スキーム(http / https),ホスト名(example.com),ポート番号,パスを結合してURIが組み立てられている。ただ, リバースプロキシを介してアクセスしている場合,実際にブラウザがアクセスしているURIはSinatraアプリケーションは知らないので,通常であればリダイレクト先として,
http://localhost:4567/dest
のようなURIを返すはず。このように返してくれれば,上でApacheに設定した

ProxyPassReverse /example-app/ http://localhost:4567/

の設定により,Apacheがブラウザに返すレスポンスでは,リダイレクト先が
http://example.com/example-app/dest
と書き換えられるはずだ。(そのためのProxyPassReverse設定)

ただ,Sinatraでは,Sinatraが親切にもリバースプロキシの有無をチェックし,リバースプロキシ経由でアクセスされている場合は,toヘルパメソッドでURIを組み立てる際に,自動的にホスト名を書き換えてくれてしまう。その結果,リダイレクト先として
http://example.com/dest
というURIが作られ,これは上記のProxyPassReverseの設定では変換されず,そのままブラウザに返されてしまう。

対処方法

Sinatraのtoヘルパメソッド(実際にはuriヘルパメソッド)のソースを見ると,

base.rb
uri << request.script_name.to_s if add_script_name

とあり, request.script_name の内容をアプリケーションのディレクトリ名(今回の例では/example-app)として使用している。であれば,この request.script_name を書き換えてあげればよさそうだ。

そこで,application.rbにbeforeフィルタを追加してやる

application.rb
before do
  request.script_name = "/example-app"
end

こうすることで,toヘルパメソッドで正しいURIが組み立てられ,想定通り
http://example.com/example-app/dest
にリダイレクトされるようになる。

上のコードではハードコードしてしまっているが,実際にはこの部分を設定ファイルなどでアプリケーションの外に出してやれば,アプリケーションの展開環境に応じたディレクトリ名を指定することができる。

別解 (Thinを使用している場合)

上の例では,アプリケーション側で対処しているが,実のところ,SinatraのアプリケーションサーバとしてThinを使っているのであれば,Thinの設定で対処することも可能。
Thinの設定ファイル(yaml)内で,

prefix: /example-app

と指定することでSinatraの request.script_name に設定される。

ただ,今回の環境では,アプリケーションサーバとしてPumaを使っているため,上記の方法が使えず,アプリケーション側で対応することにした。

6
6
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
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?