SSLと非SSLページの自動切換え

SSLアクセスして欲しいページと、非SSLアクセスして欲しいページが混在するサイト(普通のサイト)をつくるとき、以下のようになっているとユーザにも親切ですし、ものづくりが楽になります。

  • SSLでアクセスして欲しいページにHTTPでリクエストがきたらHTTPSにリダイレクト
  • SSLでアクセスして欲しいページにHTTPSでのリクエストがきたらHTTPにリダイレクト

特にものづくりにおいては、逆にこうなっていないと、HTML上にhttp://などから始まる絶対パスを書かねばならず、結構面倒なことになります。

今回は、mod_rewriteを使って上記設定をしましたというお話です。

2つの方法

さて、上記のような要件を満たす仕掛けを実現するには、以下2つのアプローチが可能です。

  1. APサーバレベルでSSL/非SSL間リダイレクトを実装。
  2. WebサーバでSSL/非SSL間リダイレクトを設定して。APサーバはSSLのことは意識せず常にhttpを処理しているつもりになる。

APサーバで処理する場合

APサーバがWEBサーバも兼ねるようなときはこの方式もアリかなと思います。また、Web/Apが分離していても開発環境と本番環境がまったく同じ環境であればこの方式に別に不便は感じないかもしれません。実際私はこれまでこの方法しか知らずにいました。Railsの場合はssl_requirementプラグインを使えば手軽に実装できます。

しかし、私はRailsやらJavaで物を作るとき、自分はEclipseを使ってlocalhost上にWebrickやらTomcatのサーバを動かして開発します。それで出来たものをSubversionにコミットして、そこから実サーバマシンにリリース&デプロイするっていうスタイルですね。また、リリース環境はApache + MongrelとかApache + Tomcatとか、必ずApacheを前に持ってくるWeb/Ap分離構成にします。そういう場合、実はこの方式はちょっとつらい。

なぜなら:

  • localhostで動かす開発用簡易サーバはSSLに対応してなかったりする。対応してたとしても、設定を覚える気にならない。
  • がんばって設定したとしても、開発用サーバは8080だとか8443だとか、80/443以外のポートで動かすことが多い。それだとURLの書き換えにおいては、httpという文字列とhttpsという文字列を入れ替えるだけでなくポート情報も書き換えなくてはならない。
  • がんばってポート番号まで書き換える実装をしたとしても、その実装は(普通は素直に80/443で使う)本番環境で役に立たない。
  • ていうか、そもそもSSLの処理(証明書発行やら暗号化・複合など)をAPサーバまで持ってくるのか?(Webサーバが暇になりすぎで実用的でないし、また、やり方もわからず、そもそもできるのかもあやしい。)

ということで自分の場合、冷静に考えれば次に述べるようにSSL/非SSLリダイレクト処理はWebサーバレベルでやりたかったわけです。が、一生懸命railsssl_requirementプラグインを試したりしてしまったのは秘密です^^

そもそも、SSL/非SSLのリダイレクトなんていうものをちゃんとやったこともなく、htmlにゴリゴリ本番環境用の絶対URLを書いて、「開発環境では動かなーい;;」なんてやっていたのも、秘密です^^

Webサーバで処理する場合

ローカルPC上のEclipseで開発して、Eclipseプラグインから起動する簡易サーバで検証してSubversionリポジトリにリリースすすようなスタイルの開発をしている場合は、こちらの方法のほうがよさそうです。即ちWebサーバでSSL/非SSL間リダイレクトを実装して、APサーバはSSLのことは意識せず常にhttpを処理しているつもりになるアーキテクチャです。

さて、私が使うWebサーバはいつでもApache。そしてApacheでリダイレクトといえば、mod_rewriteです。今回mod_rewriteを使ってリダイレクトを実現しました。

ディレクトリごとにSSL/非SSLを設定する方法を以下に記します。

たとえば、ログインと管理画面ということで、 それに対応する /login, /adminというURLパスを定義していたとしましょう。そしてそのパス以下はSSLでアクセスしてほしくて、それ以外のページは非SSLアクセスしてほしい場合を考えます。


そんなときは、以下のように書けばいいかなと思います。

<IfModule mod_rewrite.c>
        RewriteEngine On
	#ログ出力したければ・・
        #RewriteLog /var/log/apache2/rewrite.log
        #RewriteLogLevel 1  

	#SSLアクセスしてほしいページ
        RewriteCond %{SERVER_PORT} !^443$
        RewriteRule ^(/(login|admin)/.*)?$ https://%{HTTP_HOST}$1 [L,R]

	#非SSLアクセスしてほしいページ
        RewriteCond %{SERVER_PORT} ^443$
        RewriteRule !^/(login|admin) - [C]

        RewriteCond %{SERVER_PORT} ^443$
        RewriteRule ^(.*)?$ http://%{HTTP_HOST}$1 [L,R]
</IfModule>

要するにこういうことです。

	#SSLアクセスしてほしいページ
        RewriteCond %{SERVER_PORT} !^443$
        RewriteRule ^(/(login|admin)/.*)?$ https://%{HTTP_HOST}$1 [L,R]

443以外のポート(非SSL)で /login/や/admin/から始まるパスにアクセスがきたら、https://で始まるURLにリライト。

	#非SSLアクセスしてほしいページ
        RewriteCond %{SERVER_PORT} ^443$
        RewriteRule !^/(login|admin) - [C]

443ポート(SSL)で /login/や/admin/から始まらないパスにアクセスがきたら次のルールを適用([C])

        RewriteCond %{SERVER_PORT} ^443$
        RewriteRule ^(.*)?$ http://%{HTTP_HOST}$1 [L,R]

443ポート(SSL)でのリクエストはhttp://から始まるURLにリライト([L,R])


この方法ならばSSLのことを考えるのはインフラ屋さん(サーバ設定するひと)だけに任せて、アプリ屋さん(プログラム書く人)やデザイナさん(HTML作る人)はSSLのこと何も考えないですむので、中〜大規模プロジェクトなんかでもこの方法のほうが幸せなんだろうなと想像します。