Web
cloudflare
GTmetrix

GTmetrixのスコアを限界まで上げたいならCloudFlareを使おう

More than 3 years have passed since last update.

注意

  • 本投稿は、【GTmetrixのスコアを限界まで上げたいならCloudFlareは使うな - Qiita】の再検証結果について言及しています。
  • 本投稿には、GTmetrixの評価を上げる方法については何も書かれていません。
  • 本投稿の対象読者は、「出来る限りの最適化を全て行い、あとはCDNを導入すれば完璧」なサイトを作ろうとして、CloudFlareの導入を考えている方です。
  • 私は英語が読めないので、Google翻訳に頼っています。このため、誤った解釈をしている点があるかもしれません。その場合、コメントや編集リクエストにて指摘していただければありがたいです。

概要

以前、以下の内容を投稿しました。

GTmetrixのスコアを限界まで上げたいならCloudFlareは使うな - Qiita

当時は、この投稿に挙げた問題点から、GTmetrixのスコアにこだわるならばCloudFlareの使用は避けたほうが良い、というのが私の出した結論でした。

それから約1年が経過し、CloudFlareGTmetrixは大きく変更されました。
それにより私の挙げた問題点は解消され、今や使用したほうが良いという状況にまで変わっています。

本投稿では、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%)

Page Speedの結果のキャプチャ

100%。Page Speedについては完璧です。

YSlow: A(96%)

YSlowの結果のキャプチャ

Use a Content Delivery Network (CDN)を解決していないためF(40)です。
しかし、それ以外は全てA(100)。CDNが導入できれば、100%になります。

CloudFlare導入後

Page Speed: A(100%)

Page Speedの結果のキャプチャ

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.

翻訳と私の解釈が正しければ、ここには「オリジンサーバから長いキャッシュ時間を送信すると、キャッシュ期限を上げられる」と書かれています。
このため、前回エラーとなっていたGTmetrixLeverage browser cachingは解決されています。

また、以前自動で挿入されていたHTMLが挿入されなくなりました。
これにより、Minify HTMLも解決されています。

YSlow: A(99%)

YSlowの結果のキャプチャ

以前とは異なり、サーバから送信された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接続の再利用)が行われるので、このヘッダが無くても問題はないようです。

キープアライブ - Wikipedia

この機能は、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/#p04

CGI作成時に「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値に変化しています。

HTTP ETag - Wikipedia

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にキャッシュされるコンテンツにて追加されるものと考えられます。