Rails3でhttpsからhttpへのリダイレクトを行う方法

ここ最近記事書くのをサボってました.すみません.今回はSSL redirectの話.

Rails 3.1以降ではforce_sslを使うことで,サイト全体や特定のactionについて,SSLを強制することができます.
しかし,force_sslではHTTP -> HTTPSへのリダイレクトはできるのですが,その逆のHTTPS -> HTTPへのリダイレクトをサポートしていません.
つまり,force_sslを使った場合,一度httpsのURLに入ってしまった後に相対パスでリンクを遷移すると,httpアクセスで構わないページもhttpsでアクセスしてしまうことになります.
場合によってはHTTPSでアクセスする必要の無いページはできるだけHTTPでアクセスしてほしい,というケースがあるので,今回はそういったことを実現しようぜ,という話です.

このような処理が必要とされるケースとしては以下のものが考えられます.

  • アクセスの多いサイトにおいて,サーバ側でのSSL/TLSの暗号化処理が処理上のボトルネックになる場合
  • サイト上の一部のページにおいて外部HTTPサイトに置かれた画像やJavaScriptを読み込む必要がある

1番目はSSLアクセラレータやAWS ELB等を使っていれば関係ないですが,Webサーバ側でSSLサーバを兼任している場合に問題になることがあります.SSL/TLSの処理はアクセスが多いと意外と馬鹿にならないのでサイトの規模によっては必要になるでしょう.
2番目は外部APIやWidgetを使ったりするケースです.サービスによっては証明書付きのHTTPSサーバを用意していないサービスや,HTTPSのURLは有償サービスになっているものもあるため,そういったサービスを使っている場合には警告が表示されてしまいます.

そんなわけで,上記に当てはまるような問題を抱えている人はどうぞ.

Rails付属のforce_ssl

簡単にまとめると,force_sslができることは以下の二点です.

  • force_ssl指定したアクションにHTTPアクセスされた場合,HTTPSにリダイレクトさせる
  • force_ssl指定されていないアクションについては,HTTP/HTTPSどちらでもアクセスできる

図とサンプルコードにすると以下の様になります.

force_ssl

上記の例で言うと,hoge#baaなどにHTTPアクセスしているときにはHTTPのURLを行き来しているのですが,一度hoge#fooにアクセスしてHTTPSにリダイレクトしてからhoge#baaにアクセスすると,HTTPSである必要は無いのにHTTPSで通信が行われます.
ViewのリンクURLが「XXX_path」で書かれている場合は相対パスになるので,一度HTTPSに入ってしまうとHTTPに戻って来れないという状態になってしまうわけです. 

bartt/ssl_requirement

こうした問題を回避するにはbartt/ssl_requirementを使うことで,httpsアクセスさせたくないURLにアクセスされたときにはhttpリダイレクトするといった処理を実現することができます.

ssl_requirementはもともとRailsの公式ツリーの中でdhhが作成したものだったのですが,この機能がRails 3.1で取り込まれる際にforce_sslというDSLを使う形式になりました.
そのため,Railsツリーにあるssl_requirementが対応しているRailsバージョンは古く,最近のRails 3.2.13等で使うためにはfork版のbartt/ssl_requirementを使う必要があります.
基本的な使い方はGithubのREADME通りなので省略します.

bartt/ssl_requirementを使うには,DSLとしてssl_requiredとssl_allowedをcontrollerの先頭に記述することで,sample#required_actionにHTTPでアクセスすると自動的にhttps付きのURLにリダイレクトされます.
また,,sample#allowed_actionへはHTTP/HTTPSどちらでもアクセスすることができます.
一点注意としては,ssl_requirementをincludeすると,特にssl_requrired/ssl_allowedの指定が無いactionはHTTPアクセスが強要されます(HTTPSでアクセスするとHTTPにリダイレクトされる).

図にすると以下の様になります.

ssl_requirement

bartt/ssl_requirementの問題点

通常のサイトであれば,bartt/ssl_requirementを使えばやりたいことは実現できるのですが,さらにもうちょっと気の利いたことをやろうとすると,困ってしまいます.
bartt/ssl_requirementはactionに対してSSL設定の有無を決めるので,そのままではactionの内容や特定の条件でSSL設定を切り替えることができません

具体的に直面したケースとしては,PC/スマートフォン両対応サイトで,同じactionを共有しているケースで,PCの場合にはHTTP強制,スマートフォンの場合はHTTPSでアクセスさせたいといったことがありました.
PCでHTTP強制したかった理由は前述の通り外部APIの制限があったためです.

bartt/ssl_requirementをhackして融通を利かせる

bartt/ssl_requirementのデフォルトではやりたいことができなかったので元のソースを眺めてみました.
すると,SslRequirementモジュールは読み込み時にensure_proper_protocolをbefore_filterとしてセットし,SSL必須かどうかのチェックはssl_required?メソッドによって判別していることが分かりました.
使っているメソッドまで分かれば後はoverrideするだけで良いので,以下の様に実装してみました.

ssl_required_renew

これで,想定通りやりたかったことが実現できました.

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

morimorihoge

高校卒業後,学生をやりながらずっとWebアプリ開発に携わってきました.2010くらいまではPHP/Symfonyプログラマでしたが,それ以降のWeb開発はRailsほぼ一本に宗旨替えしました.開発とは別にサーバ構築・運用も10年以上やってきているので,要件定義から設計・実装・環境構築・運用まで一通り何でもこなせます.開発以外では季節により大学でWebサービス開発やプログラミング関連の非常勤講師もしており,技術の啓蒙・教育にも積極的に関わっています.最近はPM的な仕事が増えていますが,現役開発者としていつでも動ける程度にはコードもサーバも弄る日々を送っています.AWS 認定ソリューションアーキテクト – アソシエイトレベル取りました

morimorihogeの書いた記事

夏のTechRachoフェア2019

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ