さくらのレンタルサーバで常時SSL化するときに、http→httpsの転送でハマった話


前提

  • Slimframework 3を使っています。
  • さくらのレンタルサーバにアップロードしていました。
  • 公開ディレクトリの場所を変更しています。(/home/アカウント/www/ → /home/アカウント/www/ディレクトリ名/www)

動いたコード

フロントコントローラの一番上に、下記の処理を挿入しました。今回はSlimframework 3を使用していますが、CakePHPやWordPressなど、処理をフロントコントローラに集める他のフレームワークやCMSでも動きそうです。
後述のとおり、a-blog cmsの問題解決用の記事を参考にしています。
ただし、さくらのレンタルサーバでしか通用しません。
何より対応が無理矢理過ぎるのでいつ動かなくなるのかと不安です。。

index.php
if (isset($_SERVER['HTTP_X_SAKURA_FORWARDED_FOR'])) {
    $_SERVER['HTTPS'] = 'on';
    $_ENV['HTTPS'] = 'on';
}
if (empty($_SERVER['HTTPS'])) {
    header( "HTTP/1.1 301 Moved Permanently" ); 
    header("Location: https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}");
    exit;
}

mod_rewriteが思ったとおりに動かない

さくらのレンタルサーバで非SSL領域(http)へのアクセスをSSL領域(https)に飛ばそうとしました。通常は.htaccessに以下の記述で対応できます。

.htaccess
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://domain.com/$1 [R=301,L]

ところがこれでは上手く動きませんでした。

さくらのレンタルサーバーは独特な仕様をしていて、SSLの注意点が記載されていました。

「さくらのレンタルサーバ」にて提供しているウェブサーバ (Apache) は、
80番ポートを使用するものと、443番ポートを使用するものとの 2種類に分けられます。
一般的に、80番ポートは「http://」、443番ポートは「https://」という形式でアクセスされ、
「https://」についてはプロクシとして動作します。
そのため、同じディレクトリへのアクセスであっても、その際に呼び出されるウェブサーバ が異なると、
CGIプログラムやウェブサーバが.htaccessなどのファイルを読み込む際、挙動に違いが生じます。
例えば、HTTPとしてアクセスした場合はお手元のコンピュータが、
HTTPSとしてアクセスした場合はサーバそのものがアクセス元となります。
このため、SSLのみのアクセス許可(HTTPアクセスの制限)や、mod_rewriteによるURLの書き換えはできません。

SSL利用時の注意点 – さくらのサポート情報

PHPでの転送も一苦労

ならば、PHPを使った転送を試みました。
公開ディレクトリ直下のindex.php(フロントコントローラ)の一番最初にこう記述しました。

index.php
if (empty($_SERVER['HTTPS'])) {
    header( "HTTP/1.1 301 Moved Permanently" ); 
    header("Location: https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}");
    exit;
}

ですがこれも上手くいきませんでした。リダイレクトループが発生してしまいます。
httpの場合はhttpsに飛ばして、httpsの場合は上記コードは評価されない。。と思っていたのですが、さくらのレンタルサーバの場合、httpにアクセスすると、常に評価されてしまいます。
なぜかと調べてみました。

httpsのアクセスであっても、%{SERVER_PORT} に 443ではなく80が設定される。
httpsのアクセスであっても、%{SERVER_HTTPS} は空。
$_ENV['HTTPS'] に httpsのアクセスの場合は"on"が設定されるが、リライトされたものは、空になってしまう。
httpsのアクセスの場合、%{HTTP:X-Sakura-Forwarded-For} に クライアントのIPアドレスが設定される。

さくらのレンタルサーバーで常時SSL/TLSを設定する | サイト公開時のポイント | インストール | a-blog cms 制作者向け情報

特に厄介なのは「リライトされたものは、空になってしまう。」です。「\$_ENV['HTTPS']」とありますが、「\$_SERVER['HTTPS']」も同様です。つまりif文の中身が永久に実行されるようになっていたのです。

回避方法も引用元にありました。「\$_SERVER['HTTP_X_SAKURA_FORWARDED_FOR']」が設定されている場合に$_SERVERに書き込んでいます。
最終的に一番最初に挙げたコードになります。