風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

さくらのレンタルサーバ・共有SSLで .htaccess によりSSLのみのアクセス許可を設定する方法

さくらのレンタルサーバの仕様が変更になっており、この記事の内容は現状に合わなくなっている可能性が高いです。ご注意ください。
詳細は以下のページをご参照願います。
help.sakura.ad.jp
help.sakura.ad.jp
【WordPress】常時SSL化プラグインの使い方 – さくらのサポート情報



さくらのレンタルサーバで共有SSLを使用する際に、SSLのみのアクセス許可を設定(SSLアクセス強制のため、http://〜 は https://〜 へリダイレクト)するための .htaccess の記述方法を調べてみた。


設定する .htaccess の内容

[2016.07.01修正]
以下の例で、当初は

RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]」

のようにしていたが、これだと対象ディレクトリ直下で且つファイル名を指定しない(「/」で終わる)URLの場合( http://~/wordpress/ 等)にはリダイレクトされないため、修正("."→"^")。

<IfModule mod_rewrite.c>
RewriteEngine On
# --- SSLアクセスでない場合(${ENV:HTTPS} が 'on' でなく、且つ、%{HTTP:X-Sakura-Forwarded-For} が未設定の場合)には
#     https://%{SERVER_NAME}%{REQUEST_URI} へリダイレクト
RewriteCond %{ENV:HTTPS} !^on$
RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
</IfModule>


なお、WordPress を設置している場合(例として /wordpress/ 下)

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /wordpress/

# --- SSLアクセスでない場合(%{ENV:HTTPS} が 'on' でなく、且つ、%{HTTP:X-Sakura-Forwarded-For} が未設定の場合)には
#     https://%{SERVER_NAME}%{REQUEST_URI} へリダイレクト
RewriteCond %{ENV:HTTPS} !^on$
RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /wordpress/index.php [L]

</IfModule>

のようにすればよいと思われる。
WordPress の元の .htaccess の RewriteBase 行と最初のRewriteCond 行の間に、SSLアクセス強制用の RewriteCond・RewriteRule を挟み込む。 /wordpress/(二か所)は環境にあわせて書き換えること。

また、wp-config.php の「/* That's all, stop editing! Happy blogging. */(/* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */)」直前に、

if ( isset($_SERVER['HTTP_X_SAKURA_FORWARDED_FOR']) ) {
    $_SERVER['HTTPS'] = 'on';
    $_ENV['HTTPS'] = 'on';
}

/* That's all, stop editing! Happy blogging. */

という記述を追加しておく。

解説(覚え書き)

さくらのレンタルサーバで共有SSLを使用する場合、以下の点に留意する必要がある。

.htaccess からの参照時
  • %{SERVER_PORT} には、SSLかそうでないかによらず '80' が設定される。
    このため、%{SERVER_PORT}ではSSL接続かどうかの判別はできない。
    「RewriteCond %{SERVER_PORT} ^80$」や「RewriteCond %{SERVER_PORT} !^443$」は常に真となるために、リダイレクトループが発生してしまう。
  • SSLアクセス時には通常、 %{ENV:HTTPS} には 'on' が、%{HTTP:X-Sakura-Forwarded-For} にはクライアント(リクエスト元)のIPアドレスが設定される。*1
    ただし、SSLアクセスした場合であっても、mod_rewrite.c の RewriteRule によりリライトされるケースでは、リライト後には %{ENV:HTTPS} が未設定となってしまう。
    RewriteRuleの[R]フラグによりhttps://〜にリダイレクトされた場合には 'on' が設定される。


上記の .htaccess では、リダイレクトするかどうかの判別に、

RewriteCond %{ENV:HTTPS} !^on$

だけでなく、

RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$

のような条件(%{HTTP:X-Sakura-Forwarded-For}が未設定)を設定している。


これをせずに %{ENV:HTTPS} のみで判定してしまうと、例えば WordPress を設置したサイト上の http://〜/wordpress/year/month/day/ というページ(URL)にアクセスした場合、

URL %{REQUEST_URI} %{ENV:HTTPS} %{HTTP:X-Sakura-Forwarded-For} mod_rewrite.cで適用されるルール 結果
1 http://〜/wordpress/year/month/day/ /wordpress/year/month/day/ (未設定) (未設定) RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L] [A] リダイレクト
2 https://〜/wordpress/year/month/day/ /wordpress/year/month/day/ 'on' RewriteRule . /wordpress/index.php [L] [B] リライト
3 https://〜/wordpress/year/month/day/ /wordpress/index.php (未設定) RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L] [C] リダイレクト
4 https://〜/wordpress/index.php /wordpress/index.php 'on' RewriteRule ^index\.php$ - [L] [D](このまま)

のように、意図しないURLの PATH の書き換えが発生してしまう。
このため、[C] (3→4)のリダイレクトを発生させないように、条件を追加している。
むしろ「RewriteCond %{ENV:HTTPS} !^on$」の方は無くても現状では動作する。

PHP からの参照時
  • SSLアクセス時には、$_SERVER['HTTP_X_SAKURA_FORWARDED_FOR'] および $_ENV['HTTP_X_SAKURA_FORWARDED_FOR'] にはクライアント(リクエスト元)のIPアドレスが設定される。
  • SSLアクセスした場合であっても、mod_rewrite.c の RewriteRule によりリライトされるケースでは、$_SERVER['HTTPS'] と $_ENV['HTTPS'] が設定されない。
    RewriteRuleの[R]フラグによりhttps://〜にリダイレクトされた場合には 'on' が設定される。


WordPress 等では、SSL 接続かどうかを $_SERVER['HTTPS'] もしくは $_ENV['HTTPS'] の状態で判別している。
このため、上記のように $_SERVER['HTTP_X_SAKURA_FORWARDED_FOR'] が設定されているかどうかで SSL かどうかを判別し、SSL の場合には $_SERVER['HTTPS'] および $_ENV['HTTPS'] に 'on' を設定するような処理を追加してやる必要がある。

*1:将来的にさくら側で仕様変更が行われると、%{HTTP:X-Sakura-Forwarded-For}による判別が出来なくなる可能性はある。