nginx + PHP + fastCGI環境のWordPressを常時SSL化した方法


SSL/TLS

※当ブログではアフィリエイト広告を利用しています。

Googleが検索結果のランキングシグナルにHTTPSを使用することを発表して1年以上経ち、常時SSL化のサイトを見かけることも多くなったように感じます。

当サイトも常時SSL化したため、その際の手順をできるだけ詳細にメモします。

常時SSL化を導入した環境

当サイトはさくらのVPSのCentOS6.7上で nginx + PHP + fastCGI + WordPress で動作しています。nginxはバックエンドだけでなくリバースプロキシとしても使用しています。

常時SSL化する上での注意点

常時SSLするだけならそれほど手順は複雑ではありませんが、サーバ側設定の他にもいろいろ注意する点があります。主なものを以下に挙げます。

外部から読み込むリソースもHTTPS化する必要がある

常時SSL化するためにはCSSやJavaScriptや画像など、内部・外部サイト問わずhttp://で読み込んでいるリソースはすべてhttps://に変更する必要があります。

WordPressの投稿画面で画像を挿入すると自動的にhttp://からの絶対パスが付与されますがこれらもすべて変更の対象です。こちらはWordPressの「Search Regex」というプラグインで投稿内容を置換することで対応可能なので後述します。

SNSのシェア数が引き継がれない

SNSのシェア数はURLごとにユニークになっているせいかHTTPとHTTPSでは別のものとしてカウントされます。シェア数の引き継ぎは諦めようかと思ったのですが、WordPressの「SNS Count Cache」というプラグインを使えばHTTPとHTTPS両方のカウントをキャッシュとして合算して保持できることがわかったため、今回はプラグインの使用と、テーマファイルを修正してシェア数を表示するよう対応しました。

アクセス解析等のURL変更が必要

Google Search Consoleのプロパティ追加やGoogle Analyticsのプロパティ・ビュー設定が必要です。

nginx + PHP + fastCGI環境のWordPressを常時SSL化する手順

本題となる常時SSL化方法を順番に記載します。

1. 事前準備

1.1 WordPressテーマ内のURL変更

WordPressテーマ内で他サイトのリソースをhttp://で読み込んでいる部分を検索し、https://またはプロトコル相対URLに変更しておきます。

テーマ内で主に変更する必要がありそうなものは以下のとおりです。サービス側でURLを生成した時に既にHTTPSやプロトコル相対URLになっているものもあります。

サービス名 HTTP HTTPS or プロトコル相対URL
Google http://ajax.googleapis.com //ajax.googleapis.com
Google Code Archive http://html5shiv.googlecode.com //html5shiv.googlecode.com
はてな http://b.st-hatena.com https://b.st-hatena.com
Google+ なし https://apis.google.com/js/platform.js
Twitter http://platform.twitter.com //platform.twitter.com
Facebook http://connect.facebook.net //connect.facebook.net
Feedly http://s3.feedly.com https://s3.feedly.com
Pocket http://widgets.getpocket.com https://widgets.getpocket.com

※はてなはエントリ公開時点でSHA-1証明書を使用しているようでChromeで閲覧すると警告が出ます。

1.2 投稿記事内の他サイトURL変更

投稿エントリの中で他サイトの画像などを読み込んでいる部分のURLを置換します。エントリ1つ1つを置換するの大変なので、WordPressプラグインの「Search Regex」を使用して一気に置換します。

Search Regexで置換する方法は下記の画像のように「Search pattern」に置換前のURLを、「Replace pattern」に置換後のURLを入力し、「Replace & Save」ボタンを押すだけです。青の「Search」ボタンを押すと変更を保存せずに置換対象の文字列を確認することができます。

記事内のAmazonリンク変更

エントリ内で主に変更する必要がありそうなものは以下のとおりです。

サービス名 HTTP HTTPS or プロトコル相対URL
Amazonアソシエイト http://ecx.images-amazon.com/ https://images-na.ssl-images-amazon.com/
Amazonアソシエイト http://ir-jp.amazon-adsystem.com/ https://ir-jp.amazon-adsystem.com/
iTunesアフィリエイト http://axxx.phobos.apple.com/ なし

Amazonアソシエイトはhttps://のURLも用意されていたのですが、iTunesアフィリエイトで使用するアートワークやアプリのアイコンなどの画像は本エントリ公開時点でhttp://しか用意されていないようでした・・・。

iTunes Search APIのページでも返却されるURLのサンプルがhttp://となっているようなのでApple側の対応を待つしかありません。iTunesアフィリエイトの商品を紹介しているエントリではやむなくhttp://https://が混在することとなります。

1.3 SNS Count Cacheプラグインの導入

SNS Count Cacheプラグインを導入し、各エントリのSNSでのシェア数を保持できるよう準備しておきます。

SNS Count Cacheプラグインを使用するとシェア数の保持だけでなく関数からのシェア数呼び出しもできるため、独自にデザインしたSNSシェアボタンを作成することも可能です。

SNS Count Cacheを導入すると下記のように自動的にシェア数のキャッシュがスタートし、全てをキャッシュし終わるには少し時間がかかりますが、後にHTTPS化した時に再度キャッシュし直すため全てのキャッシュが完了するまで待つ必要はありません。

SNS Count Cache

2. SSLサーバ証明書の準備

2.1 SSLサーバ証明書の購入

SSLサーバー証明書は有料・無料を含めてかなり多くの種類がありますが、今回は有料のRapidSSLを使用しました。

有料といっても私が購入したのは3年で3,456円(税込)というおそらく最安値のものです。SSLストアというサイトで「ファイル認証タイプ」を選択したとき限定での価格ですが、証明書自体は通常のものと同じでアクティベーションも難しくありません。

SSLストアで格安のRapidSSL証明書を購入する方法の詳細は下記のエントリに記載しています。

関連エントリ:SSLストアで格安のSSLサーバ証明書(RapidSSLファイル認証タイプ)を購入・取得する方法

ここでSSLサーバー証明書と秘密鍵を入手します。

2.2 SSLサーバ証明書の配置

前の手順で入手したSSLサーバ証明書と秘密鍵をサーバの任意のディレクトリに配置します。

今回は以下に配置しました。

種類 パス
SSLサーバ証明書 /etc/nginx/ssl/server.crt
秘密鍵 /etc/nginx/ssl/server.key

SSLサーバー証明書は「SSLサーバ証明書」と「中間CA証明書」を連結したものとする必要があります。(SSL証明書購入時に両方とも入手できます)

「SSLサーバ証明書」と「中間CA証明書」を連結したものは以下のような中身になります。

-——BEGIN CERTIFICATE——-
ここにSSLサーバー証明書の文字列
-——END CERTIFICATE——-
-——BEGIN CERTIFICATE——-
ここに中間CA証明書の文字列
-——END CERTIFICATE——-

2.3 秘密鍵のパスフレーズ解除

秘密鍵をエディタで開いてProc-Type: 4,ENCRYPTEDなどど記載されている場合はパスフレーズで暗号化されているためそのままではアプリケーションから使用できません。

以下のコマンドでパスフレーズを解除します。encrypted.keyは複合前の、decrypted.keyは複合後のファイル名を指定します。コマンド実行するとパスフレーズを聞かれ、正しく入力すると暗号化が解除されたファイルが生成されます。

# openssl rsa -in encrypted.key -out decrypted.key

2.4 Diffie-Hellman鍵交換用パラメータファイルの準備

後にSSL安全性チェック用サイトでA+評価を取るため、Diffie-Hellman鍵交換用のパラメータファイルを作成します。
コマンドを実行後少し時間がかかり、数分で作成が完了します。

# openssl dhparam 2048 -out /etc/nginx/ssl/dhparam.pem

3. サーバ側設定変更の前準備

nginxの設定を変更する前の準備です。

3.1 iptablesで443ポートの通信を許可

HTTPS通信のためには443ポートの通信を許可する必要があるため、ポートが解放されているかを確認します。

# vi /etc/sysconfig/iptables

以下の記述があればiptablesでは443ポートの通信が許可されています。

-A INPUT -p tcp -m tcp —dport 443 -j ACCEPT

別のファイアウォールを導入している場合はそちらも443ポートの通信が可能であるかを適宜確認します。

3.2 OpenSSLのバージョン確認

OpenSSLは古いバージョンだと致命的な脆弱性を持つものがあるため、安全なパッケージがインストールされているかを確認し、そうでなければアップデートしておきます。

バージョンの確認は下記コマンドで可能です。

# openssl version

しかし上記だと最新のパッチが当たっているか分からないため、下記コマンドでインストール済みパッケージと利用可能なパッケージを比較します。

# yum list openssl

4. HTTPS通信を有効化

4.1 nginxの設定変更(HTTPS通信の確認用)

まずはSSLサーバ証明書が正しく機能してHTTPSで通信可能なことを確認するため、HTTPとHTTPS両方でアクセス可能な状態とします。

nginx設定ファイルの既存のserverブロックにSSL関連の設定を追加します。

server {
    listen       80 default;
    listen       443 ssl;
    server_name  _;
    root    /var/www/html/example.com;
    index   index.html index.htm index.php;
    charset utf-8;

    # SSL
#    ssl on;
    ssl_certificate      /etc/nginx/ssl/server.crt;
    ssl_certificate_key  /etc/nginx/ssl/server.key;
    # TLS1.0 1.1 1.2のみ有効
    ssl_protocols        TLSv1 TLSv1.1 TLSv1.2;
    # サーバが提示した暗号スイートを優先する
    ssl_prefer_server_ciphers on;

    # 安全でない暗号化スイートはサポートしない
    ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED';
    # SSLセッションキャッシュとサイズを指定
    ssl_session_cache    builtin:1000 shared:SSL:10m;
    # SSLセッションキャッシュのタイムアウトまでの時間
    ssl_session_timeout  10m;
    # DH鍵交換パラメータファイル
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    # OCSP応答をサーバー側にキャッシュ
    ssl_stapling on;

#以降は既存の設定のまま

私が最初に実施した時はssl onにしていたことが原因で「The plain HTTP request was sent to HTTPS port」というエラーが発生しました。この時点ではHTTPもHTTPSも有効にしたいため一旦コメントアウトします。

設定変更が完了したら下記コマンドで設定ファイルの再読み込みを行います。

# nginx -s reload

4.2 WordPressのURL変更

4.1でHTTPSで通信可能なことが確認できたらWordPressの管理画面で「設定」→「一般」よりWordPressアドレス(URL)とサイトアドレス(URL)をhttps://に変更します。

WordPress httpsに変更

4.3 nginxの設定変更(HTTP→HTTPSリダイレクト)

HTTPからHTTPSにリダイレクトするよう設定変更します。一つだったserverブロックがHTTP用とHTTPS用の2つに分かれます。

また、HTTPでアクセスされた場合に次回以降はHTTPSを使うようブラウザに伝えるため、HTTP Strict Transport SecurityのHTTPヘッダを追加します。

server {
    listen       80;
    server_name  _;
    return 301 https://$host$request_uri;
}

server {
    listen       443 ssl;
    server_name  _;
    root    /var/www/html/example.com;
    index   index.html index.htm index.php;
    charset utf-8;

    # SSL
    ssl on;
    ssl_certificate      /etc/nginx/ssl/server.crt;
    ssl_certificate_key  /etc/nginx/ssl/server.key;
    # TLS1.0 1.1 1.2のみ有効
    ssl_protocols        TLSv1 TLSv1.1 TLSv1.2;
    # サーバが提示した暗号スイートを優先する
    ssl_prefer_server_ciphers on;

    # 安全でない暗号化スイートはサポートしない
    ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED';
    # SSLセッションキャッシュとサイズを指定
    ssl_session_cache    builtin:1000 shared:SSL:10m;
    # SSLセッションキャッシュのタイムアウトまでの時間
    ssl_session_timeout  10m;
    # DH鍵交換パラメータファイル
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    # OCSP応答をサーバー側にキャッシュ
    ssl_stapling on;
    
    #HTTP Strict Transport Security
    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';

#以降は既存の設定のまま

Strict-Transport-SecurityincludeSubDomainsはすべてのサブドメインがHTTPSの場合のみ追加します。またpreloadは後述のHSTS Preload Listに登録する場合に追加します。

4.4 nginxの設定変更(FastCGIパラメータでSSLを有効化)

nginx + FastCGI環境だと、ここまでの設定だけだと以下のような状態になることがあります。

  • トップページにアクセスするとリダイレクトループが発生する
  • 管理画面にアクセスするとリダイレクトループが発生する
  • bloginfo('stylesheet_url')などで取得するURLがhttp://になる
  • the_post_thumbnail()でサムネイルが正しく取得できない

この場合、FastCGIパラメータの設定をしている箇所にfastcgi_param HTTPS on;を追加すると問題を回避できます。

参考までに当サイトの該当箇所の設定は以下のようになっています。

    location ~ \.php$ {
        try_files $uri =404;
        expires        off;
        fastcgi_pass   phpfpm;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
        fastcgi_param  REMOTE_ADDR      $http_x_real_ip;
        fastcgi_param  HTTPS on;
        fastcgi_pass_header "X-Accel-Redirect";
        fastcgi_pass_header "X-Accel-Buffering";
        fastcgi_pass_header "X-Accel-Charset";
        fastcgi_pass_header "X-Accel-Expires";
        fastcgi_pass_header "X-Accel-Limit-Rate";
    }

設定変更が完了したら下記コマンドで設定ファイルの再読み込みを行います。

# nginx -s reload

5. 常時SSL化後にやること

5.1 記事内のサイト内リンク変更

WordPress管理画面より「Search Regex」で、時サイト内のリンクをhttp://からhttps://に置換します。

以下は当サイトでの置換例です。
記事内のサイト内リンク変更

5.2 SNS Count CacheでHTTPSへのスキーム移行モードを設定

プラグイン「SNS Count Cache」の設定で「HTTPからHTTPSへのスキーム移行モード」を「有効」に、「HTTPからHTTPSへのスキーム移行日」に日付を設定し、設定を更新します。

SNS Count CacheでHTTPSへのスキーム移行モードを設定

この設定を行うことでhttp://でシェアされた数とhttps://でシェアされた数を合算することが可能です。

5.3 Google Search ConsoleへURL登録

Google Search Console(旧ウェブマスターツール)にhttps://のURLを登録し、サイトマップを送信します。

Google Search ConsoleへURL登録

5.4 Google AnalyticsのURL変更

Google Analyticsのアナリティクス設定で、「プロパティ設定」と「ビュー設定」のURLをhttps://に変更します。

Google AnalyticsのURL変更

5.5 HSTS preload listへのドメイン登録

nginxの設定でHTTP Strict Transport SecurityのHTTPヘッダを追加した際、preloadというパラメータを追加しました。

その上でGoogleが実施しているHSTS preload listに自分のドメインを登録すると、Chromeなどの主要なブラウザと自分のドメインが常にHTTPSで通信する必要があることを伝えることができます。

そのため下記サイトで自分のドメインの登録申請をしておきます。ただしincludeSubDomainsパラメータも必要となるため、すべてのサブドメインがHTTPSでない場合は登録できないようです。

HSTS Preload Submission

常時SSL化した後の「Qualys SSL Labs」での評価

SSLが有効なサーバを解析し、適切な設定がされているかを評価する「Qualys SSL Labs SSL Server Test」で設定内容をチェックしてみました。

苦労の甲斐あってか「A+」の評価をゲットすることができました。
Qualys SSL Labs A+評価

参考にさせて頂いた他サイト

nginxの設定関連など、今回かなり多くのサイトの記事に助けられました。参考にさせて頂いたサイトは以下の通りです。

おわりに

FastCGIの設定漏れでかなりハマったため、サイトの表示がおかしくなる時間をかなり長く作ってしまいましたが無事に常時SSL化できて一安心です。

Googleのランキングシグナルに使用されるとはいえ、コンテンツの質に比べれば重要度は低いため、ユーザへの安全性提供と自己満足が今回の主な効果だと思います。