[Perl] MojoliciousでHTTP(S)の振り分けをリバースプロキシのバックエンドでも出来るようにしてみる
- 2013.11.06
- Mojolicious
- Mojolicious, Nginx, webアプリケーション, リバースプロキシ
![[Perl] MojoliciousでHTTP(S)の振り分けをリバースプロキシのバックエンドでも出来るようにしてみる](https://perl.no-tubo.net/wp-content/uploads/2013/04/mojolicious.png) 
		  		        
      追記(解決編)
@clicktx 各routeの判別でリクエストのスキームではなくX-Forwarded-HTTPSを見て判別するように変更してもダメですかね?Apache+mod_proxyの例ですが、これとは意図がちがうのかな? https://t.co/VMvpP3vd1C
— Hayato Imai (@hayajo) November 12, 2013
という事で、検証して頂きまして、うまくいかない原因が判明しました。以下のgistが最高に参考になります。@hayajo++
以下原文
SSL専用のページとどちらでもいいページとSSLでアクセスして欲しくないページをrouterのところで出来れば楽だと考えて、試行錯誤してみたけどなかなかうまく行きません。
以前の記事
- Mojoliciousでhttps(SSL)専用ページとhttp専用ページの振り分けをroutesのbridgeを使って行う
- mojoliciousでSSLページヘリダイレクトさせると無限ループしてしまう問題
結論から言うとHTTP -> HTTPSの場合はなんとかうまくいきます。が、HTTPS -> HTTPの場合はどうしてもうまくいかない。
redirect_toメソッドでリダイレクトする場合、同ホスト・同じpathに限りLocationヘッダに設定されるURLがなぜかhttpsとなってしまう = リダイレクトが無限ループする。...なぜだ??
なので、mataタグを使ったリダイレクトでその場しのぎをしてみます。
Nginxの設定
proxy_set_header X-Forwarded-HTTPS を使えばいいのではとコメント頂いて助かりました。ただ、proxy_set_header X-Forwarded-HTTPS 1(on);とかだと常にHTTPSの通信と伝えられてしまうみたいなので、
  location / {
          if ($scheme = "https"){
              set $mode "on";
          }
          proxy_set_header X-Forwarded-HTTPS $mode;
  }
のように条件式を入れて使っています。
リダイレクト用 テンプレートを用意する
HTTPS -> HTTP にリダイレクトする場合にどうしてもうまくいかないのでリダイレクト用のテンプレートを使います。
<head>
  <meta http-equiv="refresh" content="0;url=<%= $self->req->url->to_abs->scheme('http') %>">
</head>
mojolicious
sub startup {
  $self = shift;
....
  # Routes
  my $routes = $self->routes;
  # SSL onry
  my $ssl_rotes = $routes->under(
    sub{
      my $self = shift;
      return 1 if $self->req->is_secure;
      $self->redirect_to($self->req->url->to_abs->scheme('https'));
    }
  );
  # http onry
  my $not_ssl_routes = $routes->under(
    sub{
      my $self = shift;
      return 1 if !$self->req->is_secure;
      # $self->redirect_to($self->req->url->to_abs->scheme('http')); # これだとリダイレクトが無限ループする
      $self->render(template =>'redirect');
    }
  );
  # SSL onry route
  $ssl_rotes->route('/login')->to('login#index');
  $ssl_rotes->route('/join')->to('join#index');
  # not SSL routes
  $not_ssl_routes->route('/')->to('top#index');
  $not_ssl_routes->route('/entry/:id', id => qr/\d+/)->to('entry#index', id => 1 );
  # Any Routes
  $routes->route('/:controller')->to(action => 'index');
  $routes->route('/:controller/:action')->to();
  $routes->route('/:controller/:action/:id')->to();
}
なんかイマイチ納得行かないけれど妥協。
おまけ
確認方法
opensslを使ってコマンドラインで確認してみるとLocationヘッダがhttpsになっているのが確認できる。
$ openssl s_client -connect localhost:443
...
    Start Time: 1383817749
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)
---
GET / HTTP/1.1
Host:local
HTTP/1.1 302 Found
Server: nginx/1.4.3
Date: Thu, 07 Nov 2013 09:49:29 GMT
Content-Length: 0
Location: https://local/
Connection: keep-alive
      - 
            前の記事  MacPortsでNginxをインストールして起動してみる 2013.11.03
- 
            次の記事  Gunma.web#15でしゃべりました 2014.01.05