注意
- 本投稿は、【GTmetrixのスコアを限界まで上げたいならCloudFlareは使うな - Qiita】の再検証結果について言及しています。
- 本投稿には、GTmetrixの評価を上げる方法については何も書かれていません。
- 本投稿の対象読者は、「出来る限りの最適化を全て行い、あとはCDNを導入すれば完璧」なサイトを作ろうとして、CloudFlareの導入を考えている方です。
- 私は英語が読めないので、Google翻訳に頼っています。このため、誤った解釈をしている点があるかもしれません。その場合、コメントや編集リクエストにて指摘していただければありがたいです。
概要
以前、以下の内容を投稿しました。
GTmetrixのスコアを限界まで上げたいならCloudFlareは使うな - Qiita
当時は、この投稿に挙げた問題点から、GTmetrixのスコアにこだわるならばCloudFlareの使用は避けたほうが良い、というのが私の出した結論でした。
それから約1年が経過し、CloudFlareやGTmetrixは大きく変更されました。
それにより私の挙げた問題点は解消され、今や使用したほうが良いという状況にまで変わっています。
本投稿では、2015年9月現在最新のCloudFlare及びGTmetrixを使用し、以前の投稿内容について再検討します。
実際のスコア
計測対象のページについて
- PHPにより動的に生成したHTMLから、JavaScriptファイル1つ、画像ファイル5つを読み込んでいるページです。
- JavaScriptと画像は、適切なHTTPヘッダを追加するためPHPを経由して出力しています。
- CSSはHTMLに含まれています。このため、CSSファイルの結果は存在しません。
計測結果について
- プレビュー画像にリンクを貼っています。リンク先はページ全体のキャプチャ画像です。
- 作成中のサイトであるため、GTmetrix内のプレビュー画像とURIは伏せています。
- 作成中のサイトであるため、ページのドメインは
www.example.net
、JavaScriptと画像のドメインはc.example.net
とします。(ヘッダ内に含まれるドメイン名も、これに合わせたものに修正します) - キャプチャはFull Page Screen Captureで撮影しています。
CloudFlare導入前
Page Speed: A(100%)
100%。Page Speedについては完璧です。
YSlow: A(96%)
Use a Content Delivery Network (CDN)を解決していないためF(40)です。
しかし、それ以外は全てA(100)。CDNが導入できれば、100%になります。
CloudFlare導入後
Page Speed: A(100%)
CloudFlareの設定で、わざとキャッシュ期限を2時間としています。
しかし、実際に送信されるExpiresヘッダの値は、サーバから送信している30日のキャッシュ期限でした。
CloudFlare設定ページの項目「Browser Cache Expiration」のHelpにも、以下のようにあります。
Note: You can also increase the cache expiration by specifying a longer cache time on the origin server, or you can set a different Cache Expiration for a specific path or resource using the Page Rules app.
翻訳と私の解釈が正しければ、ここには「オリジンサーバから長いキャッシュ時間を送信すると、キャッシュ期限を上げられる」と書かれています。
このため、前回エラーとなっていたGTmetrixのLeverage browser cachingは解決されています。
また、以前自動で挿入されていたHTMLが挿入されなくなりました。
これにより、Minify HTMLも解決されています。
YSlow: A(99%)
以前とは異なり、サーバから送信されたHTTPヘッダがほぼ維持されているため、ExpiresヘッダやETagヘッダがほぼそのまま保持されています。
これにより、前回エラーとなっていたAdd Expires headers及びConfigure entity tags (ETags)が解決されています。
前回同様、Use cookie-free domainsはエラーとなります。
しかし、本命であるUse a Content Delivery Network (CDN)はUse cookie-free domainsより優先度が高いため、結果としてスコアも上がっています!
(Use cookie-free domainsのPRIORITYはLOWなのに対し、Use a Content Delivery Network (CDN)はMEDIUMです。)
CloudFlare導入によるHTTPレスポンスヘッダの変化
※CSSはHTMLに埋め込んでいるため、結果はありません。
※一部のヘッダーに含まれるサイトのドメインは、www.example.net
及びc.example.net
に置き換えています。
CloudFlare導入前
HTML
HTTP/1.1 200 OK
Accept-Ranges:bytes
Cache-Control:no-cache, must-revalidate
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:3308
Content-Security-Policy:default-src 'self' c.example.net; style-src 'self' 'unsafe-inline'; script-src 'self' c.example.net; img-src 'self' 'unsafe-inline' c.example.net; report-uri /csp-report
Content-Type:text/html;charset=utf-8
Date:Sat, 05 Sep 2015 18:58:45 GMT
Expires:Mon, 26 Jul 1997 05:00:00 GMT
Keep-Alive:timeout=2, max=20
Last-Modified:Sat, 05 Sep 2015 18:58:46 GMT
pragma:no-cache
Server:Apache
Set-Cookie:c=1; expires=Tue, 05-Sep-2017 06:36:18 GMT; Max-Age=63113852; path=/
Vary:Accept-Encoding
X-Content-Type-Options:nosniff
x-dns-prefetch-control:on
X-Download-Options:noopen
X-Frame-Options:SAMEORIGIN
X-UA-Compatible:IE=edge,chrome=1
X-WebKit-CSP:default-src 'self' c.example.net; style-src 'self' 'unsafe-inline'; script-src 'self' c.example.net; img-src 'self' 'unsafe-inline' c.example.net; report-uri /csp-report
X-XSS-Protection:1; mode=block
JavaScript
HTTP/1.1 200 OK
Accept-Ranges:bytes
Access-Control-Allow-Origin:*
Cache-Control:public, max-age=2592000
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:21222
Content-Type:application/javascript
Date:Sat, 05 Sep 2015 18:58:46 GMT
Etag:"Ob7qSMYEipoDyD0kPs/jHx5OstD6YrqTH8BNxlIztzur3xupuh9G4qtX2QGidzlQcQsYrT+Ym3jfSt0e6QQ2DA"
Expires:Mon, 05 Oct 2015 18:58:47 GMT
Keep-Alive:timeout=2, max=20
Last-Modified:Sat, 24 Jan 2015 08:07:40 GMT
Server:Apache
Vary:Accept-Encoding
X-Content-Type-Options:nosniff
X-Download-Options:noopen
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
画像
HTTP/1.1 200 OK
Accept-Ranges:bytes
Cache-Control:public, max-age=2592000
Connection:Keep-Alive
Content-Length:8307
Content-Type:image/png
Date:Sat, 05 Sep 2015 18:58:46 GMT
Etag:"3i7Gtvyz91OC4yPTJhDPn+uZC6q+JSVl7jT0RxI6tWlpDbW9qJKC4/p+HNSmfVE5YfetgpVFXzvya4+aPXv5EA"
Expires:Mon, 05 Oct 2015 18:58:47 GMT
Keep-Alive:timeout=2, max=20
Last-Modified:Wed, 22 Oct 2014 05:48:16 GMT
Server:Apache
X-Content-Type-Options:nosniff
X-Download-Options:noopen
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
CloudFlare導入後
以前とは異なり、サーバから送信されたX-Content-Type-Options: nosniff
ヘッダ等も維持されています。
HTML
HTTP/1.1 200 OK
Cache-Control:no-cache, must-revalidate
CF-RAY:2214475731732096-KIX
Connection:keep-alive
Content-Encoding:gzip
Content-Security-Policy:default-src 'self' c.example.net; style-src 'self' 'unsafe-inline'; script-src 'self' c.example.net; img-src 'self' 'unsafe-inline' c.example.net; report-uri /csp-report
Content-Type:text/html;charset=utf-8
Date:Sat, 05 Sep 2015 19:18:12 GMT
Expires:Mon, 26 Jul 1997 05:00:00 GMT
Last-Modified:Sat, 05 Sep 2015 19:18:12 GMT
pragma:no-cache
Server:cloudflare-nginx
Set-Cookie:c=1; expires=Tue, 05-Sep-2017 06:55:44 GMT; Max-Age=63113852; path=/
Transfer-Encoding:chunked
Vary:Accept-Encoding
X-Content-Type-Options:nosniff
x-dns-prefetch-control:on
X-Download-Options:noopen
X-Frame-Options:SAMEORIGIN
X-UA-Compatible:IE=edge,chrome=1
X-WebKit-CSP:default-src 'self' c.example.net; style-src 'self' 'unsafe-inline'; script-src 'self' c.example.net; img-src 'self' 'unsafe-inline' c.example.net; report-uri /csp-report
X-XSS-Protection:1; mode=block
JavaScript
HTTP/1.1 200 OK
Access-Control-Allow-Origin:*
Cache-Control:public, max-age=2592000
CF-Cache-Status:HIT
CF-RAY:2214475c5143204e-KIX
Connection:keep-alive
Content-Encoding:gzip
Content-Type:application/javascript
Date:Sat, 05 Sep 2015 19:18:13 GMT
Etag:W/"Ob7qSMYEipoDyD0kPs/jHx5OstD6YrqTH8BNxlIztzur3xupuh9G4qtX2QGidzlQcQsYrT+Ym3jfSt0e6QQ2DA"
Expires:Mon, 05 Oct 2015 19:18:13 GMT
Last-Modified:Sat, 24 Jan 2015 08:07:40 GMT
Server:cloudflare-nginx
Transfer-Encoding:chunked
Vary:Accept-Encoding
X-Content-Type-Options:nosniff
X-Download-Options:noopen
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
画像
HTTP/1.1 200 OK
Accept-Ranges:bytes
Cache-Control:public, max-age=2592000
CF-Cache-Status:HIT
CF-RAY:2214475c5d7e2096-KIX
Connection:keep-alive
Content-Length:8307
Content-Type:image/png
Date:Sat, 05 Sep 2015 19:18:13 GMT
Etag:"3i7Gtvyz91OC4yPTJhDPn+uZC6q+JSVl7jT0RxI6tWlpDbW9qJKC4/p+HNSmfVE5YfetgpVFXzvya4+aPXv5EA"
Expires:Mon, 05 Oct 2015 19:18:13 GMT
Last-Modified:Wed, 22 Oct 2014 05:48:16 GMT
Server:cloudflare-nginx
Vary:Accept-Encoding
X-Content-Type-Options:nosniff
X-Download-Options:noopen
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
差分
※計測時間の差により、増減していないヘッダも変化しています。
HTML
HTTP/1.1 200 OK
-Accept-Ranges:bytes
Cache-Control:no-cache, must-revalidate
+CF-RAY:2214475731732096-KIX
-Connection:Keep-Alive
+Connection:keep-alive
Content-Encoding:gzip
-Content-Length:3308
Content-Security-Policy:default-src 'self' c.example.net; style-src 'self' 'unsafe-inline'; script-src 'self' c.example.net; img-src 'self' 'unsafe-inline' c.example.net; report-uri /csp-report
Content-Type:text/html;charset=utf-8
-Date:Sat, 05 Sep 2015 18:58:45 GMT
+Date:Sat, 05 Sep 2015 19:18:12 GMT
Expires:Mon, 26 Jul 1997 05:00:00 GMT
-Keep-Alive:timeout=2, max=20
-Last-Modified:Sat, 05 Sep 2015 18:58:46 GMT
+Last-Modified:Sat, 05 Sep 2015 19:18:12 GMT
pragma:no-cache
-Server:Apache
+Server:cloudflare-nginx
-Set-Cookie:c=1; expires=Tue, 05-Sep-2017 06:36:18 GMT; Max-Age=63113852; path=/
+Set-Cookie:c=1; expires=Tue, 05-Sep-2017 06:55:44 GMT; Max-Age=63113852; path=/
+Transfer-Encoding:chunked
Vary:Accept-Encoding
X-Content-Type-Options:nosniff
x-dns-prefetch-control:on
X-Download-Options:noopen
X-Frame-Options:SAMEORIGIN
X-UA-Compatible:IE=edge,chrome=1
X-WebKit-CSP:default-src 'self' c.example.net; style-src 'self' 'unsafe-inline'; script-src 'self' c.example.net; img-src 'self' 'unsafe-inline' c.example.net; report-uri /csp-report
X-XSS-Protection:1; mode=block
JavaScript
HTTP/1.1 200 OK
-Accept-Ranges:bytes
Access-Control-Allow-Origin:*
Cache-Control:public, max-age=2592000
+CF-Cache-Status:HIT
+CF-RAY:2214475c5143204e-KIX
-Connection:Keep-Alive
+Connection:keep-alive
Content-Encoding:gzip
-Content-Length:21222
Content-Type:application/javascript
-Date:Sat, 05 Sep 2015 18:58:46 GMT
+Date:Sat, 05 Sep 2015 19:18:13 GMT
-Etag:"Ob7qSMYEipoDyD0kPs/jHx5OstD6YrqTH8BNxlIztzur3xupuh9G4qtX2QGidzlQcQsYrT+Ym3jfSt0e6QQ2DA"
+Etag:W/"Ob7qSMYEipoDyD0kPs/jHx5OstD6YrqTH8BNxlIztzur3xupuh9G4qtX2QGidzlQcQsYrT+Ym3jfSt0e6QQ2DA"
-Expires:Mon, 05 Oct 2015 18:58:47 GMT
+Expires:Mon, 05 Oct 2015 19:18:13 GMT
-Keep-Alive:timeout=2, max=20
Last-Modified:Sat, 24 Jan 2015 08:07:40 GMT
-Server:Apache
+Server:cloudflare-nginx
+Transfer-Encoding:chunked
Vary:Accept-Encoding
X-Content-Type-Options:nosniff
X-Download-Options:noopen
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
画像
HTTP/1.1 200 OK
Accept-Ranges:bytes
Cache-Control:public, max-age=2592000
+CF-Cache-Status:HIT
+CF-RAY:2214475c5d7e2096-KIX
-Connection:Keep-Alive
+Connection:keep-alive
Content-Length:8307
Content-Type:image/png
-Date:Sat, 05 Sep 2015 18:58:46 GMT
+Date:Sat, 05 Sep 2015 19:18:13 GMT
Etag:"3i7Gtvyz91OC4yPTJhDPn+uZC6q+JSVl7jT0RxI6tWlpDbW9qJKC4/p+HNSmfVE5YfetgpVFXzvya4+aPXv5EA"
-Expires:Mon, 05 Oct 2015 18:58:47 GMT
+Expires:Mon, 05 Oct 2015 19:18:13 GMT
-Keep-Alive:timeout=2, max=20
Last-Modified:Wed, 22 Oct 2014 05:48:16 GMT
-Server:Apache
+Server:cloudflare-nginx
+Vary:Accept-Encoding
X-Content-Type-Options:nosniff
X-Download-Options:noopen
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
結論
HTMLを自前でMinifyしており、
コンテンツも自前でgzip圧縮し、
HTTPヘッダも適切に指定し、
コンテンツをわざわざ別ドメインで配信し、
それによってAA評価を得ている場合は、
さらなる高評価のためCloudFlareの導入を考えてください。
別ドメインで配信する労力は無駄になりますが、評価が上昇します。
備考
以降は、GTmetrixの評価とはあまり関連性のない、CloudFlareによる影響について記述します。
CloudFlareの出力にKeep-Aliveヘッダが無い
差分をみると分かるように、いずれのレスポンスからもKeep-Aliveヘッダが無くなっています。
これによる影響について調べた所、HTTP 1.1ではConnectionヘッダで明示されない限り自動でkeep-alive(TCP接続の再利用)が行われるので、このヘッダが無くても問題はないようです。
この機能は、HTTP 1.0では"Connection: Keep-alive"ヘッダを入れることで有効になったが、HTTP 1.1ではデフォルトで有効になっている
【HTTP】Keep Alive のメモ at softelメモ
HTTP 1.1 では、すべての接続がkeep-alive。
次のようなヘッダが送信されない限りは keep-alive。
Connection: close
なので、
Connection: Keep-Alive
のヘッダはもう意味がない。
また、Keep-Aliveヘッダは無い方が良いという情報も見つけました。
【HTTP】Keep Alive のメモ at softelメモ
Keep-Alive:
というオプションのヘッダが RFC2068 にあるが、きちんと定義されておらず、ほとんど意味がない。使用を避けるべし。
このため、CloudFlareによるKeep-Aliveヘッダの削除は有用と言えるでしょう。
CloudFlareの出力からContent-Lengthヘッダが消え、HTTP Chunked transfer encodingに変わっている
差分をみると分かるように、HTMLとJavaScriptのレスポンスからContent-Lengthヘッダが消え、Transfer-Encodingヘッダが追加されたHTTP Chunked transfer encodingによる送信に変わってしまっています。
HTTP Chunked transfer encodingはストリーミング等の特殊な用途以外では、利用しないほうが良いものです。
HTTP Chunked transfer encodingの各チャンクサイズの決定方法は?(3950)|teratail
Content-Length: xxx
の計算が容易なため、(TCPレベルで見たときの)通信が1回で済むこちらを使うべきでしょう。Transfer-Encoding: Chunked
の出る幕はありません。
PHP での HTTP レスポンスを Transfer-Enconding: Chunked から Content-Length 指定に変えたい - tilfin's note
そもそもチャンクなのもどうのかなぁと思っていたら、
DoCoMo のiモードコンテンツ作成時の仕様 HTTPより
http://www.nttdocomo.co.jp/service/imode/make/content/html/notice/basis/#p04CGI作成時に「Content-Type」、「Content-Length」は必須項目となります。
とあるので、ケータイからアクセス対応も考えるとチャンクではなく Content-Length で対応しないと駄目ということです。
この問題について調べた所、以下のページを見つけました。
Why is my dynamic content being sent with chunked encoding? – CloudFlare Support
ここによると、特定の拡張子のコンテンツのみをCloudFlareはキャッシュし、それ以外はキャッシュせずHTTP Chunked transfer encodingとgzip圧縮で送信するとのこと。
HTMLについては確かに拡張子が未設定なため、これにも合致します。
しかし、JavaScriptはキャッシュされる拡張子を持っているにも関わらず、キャッシュされていません。
解決策についても書かれているようなのですが、適切な翻訳と解釈が出来ないため不明です。
CloudFlareの出力のEtagヘッダの値が、弱いETag値に変わっている
差分をみると分かるように、JavaScriptのレスポンスのEtagヘッダにW/
が追加され、弱いETag値に変化しています。
ETagには強いETag値と弱いETag値が存在する。表記としては、頭に「W/」が付くものが弱いETag値、付かないものが強いETag値である[3]。
"123456789" -- 強いETag値 W/"123456789" -- 弱いETag値
強いETag値が一致すれば、2つのリソースが1バイトも変わらず、またContent-Languageなどのヘッダも変わっていないことを示す。強いETagはキャッシュや部分リクエストにも使いうる。
弱いETag値が一致した場合、2つのリソースは意味合いとして同じ、つまり実用的にはキャッシュされたものを代用できる、ということを示している。ただ、1バイトも変わらず同じであることは保証されず、部分リクエストのバリデーションには使えない。これは、Webページを動的に生成する場合など、強いETagをサーバで生成することが現実的でない場合に使うことができる。
恐らく、CloudFlareの出力からContent-Lengthヘッダが消え、HTTP Chunked transfer encodingに変わっているでも書いたように、JavaScriptがCloudFlareにキャッシュされていない事が原因であると考えられます。
CloudFlareの出力にAccept-Rangesヘッダが無い
差分をみると分かるように、HTMLとJavaScriptのレスポンスからAccept-Rangesヘッダが消えてしまっています。
Accept-RangesヘッダはRange Requestsに対応している場合に送信するヘッダです。
画像ではAccept-Rangesヘッダが保持されている(あるいは、CloudFlareが追加した)点から、CloudFlareの出力からContent-Lengthヘッダが消え、HTTP Chunked transfer encodingに変わっているでも書いたようなCloudFlareにキャッシュされるコンテンツにて追加されるものと考えられます。