PageSpeedを使ってWebサイトを最適化・高速化しよう

04-chrome-plugin

Google DevelopersのプロダクトにPageSpeedというWebサイト最適化ツールがある。診断&アドバイスのツールと、HTMLなどを自動変換するツールが用意されており、これらのいずれかを使うことによって的確に既存のWebサイトの高速化が可能だ。診断結果をベースに作業ポイントを紹介していこう。

PageSpeedはモバイルデバイスを強く意識するようになった

 Webサイトの最適化アドバイスツールのPageSpeedは、WebブラウザFirefoxのプラグイン(拡張機能)としてGoogleから提供されてきた。同様のツールとしては米Yahoo!が提供するYSlowがある。昔はYSlowおよびYUI関連のツールを使うぐらいしか手頃な最適化手法はなかったが、2009年にPageSpeedが登場し、その後PageSpeedはGoogle Chromeの拡張機能としても利用できるようになって選択肢が広がった。さらに現在は、サイトサービスとして利用でき、ApacheやNginxのモジュールとしてPageSpeedのルールによる自動最適化までも行えるようになっている。

01-img-detail

図1 PageSpeedによるアドバイス表示。具体的にどのファイルを修正するかまで解説される

 PageSpeedが指摘するWebサイト最適化ポイントを大まかに列挙すると以下のようになる。

  • 画像ファイルの最適化(縮小)
  • CSSスプライトの利用
  • ブラウザキャッシュのための適切なHTTPレスポンスの適用
  • JavaScriptやCSSの縮小化や統合

 Webサイトの高速化を気にしている人にとっては昔からの常識であり、サイト構築のコーディングの時点で対応している人も多いだろう。しかし、管理するサイトが増えてきたり、ページが増えていくと、必ず漏れが発生するものだ。ある程度慣れた人でも、PageSpeedのようなチェックツールは利用しておくと便利な場面は多い。

 また、こういったアドバイスツールを使い続けることは、最適化手法のトレンドを吸収するうえでも有効だ。最近の大きな変化としては、PageSpeedがモバイルデバイスを強く意識した構成となり、評価がモバイルとPCにわかれたほか、JavaScriptやCSSの設置方法を厳密に指示するようになった。具体的には、以前はJavaScriptを<head>タグ内に配置しても問題とされていなかったが、JavaScriptの処理を意識したHTML構成で使われる「</body>直前にJavaScriptを置く」というルールを適用するようになった。JavaScriptによる一時的なレンダリング(描画)ブロックも重要な問題だというわけだ(詳細は後述)。

02-js-err

図2 必要項目にレンダリングブロックの修正が追加された

 さらには、CSSを<head>内に統合化してファイル縮小するといった旧来の単純なアドバイスではなく、該当ページに「不要なCSSコードを置くな、インライン化せよ」というところまで解析するようになった。PageSpeedは日々進化しているのである。

PageSpeed診断ツールの利用方法

 PageSpeedプロダクトしてWebサーバー用の自動変換モジュールが追加されているので、「PageSpeed」といっても少し意味が広がってしまったが、旧来からの「診断ツール」を利用するには、Google ChromeかFirefoxの拡張機能としてインストールするか、サイトサービスのページにアクセスをしてURLを入力するかの2つとなる。

使い分けを考慮

 当初はWebブラウザの拡張機能として提供されてきたPageSpeedだが、ブラウザ拡張機能のほうは開発が滞っているようで、サイトサービスのほうが優先して実装されている状態である。近いうちにブラウザ拡張機能のほうも追従する予定とはなっているが、2014年1月時点ではまだ追従できていない。最新の診断ルールを適用する場合はサイトサービスを使うほうが良いだろう。

 一方で新しくなったサイトサービスの機能であるが、アドバイス時のファイル圧縮のサンプルを提示しないという違いもある。ブラウザ拡張機能での診断では、たとえばPNGファイルが最適化されていない場合、最適化例として該当ファイルを最適化したファイルも作成、提示される。そのため、提示されたファイルに差し替えればその部分の最適化作業は完了するが、この作業フローはサイトサービスでは利用できなくなってしまっている。

 このようにブラウザ拡張機能とサイトサービスで違いが出てきてしまっており、ChromeとFirefoxの実装でも違いが発生してしまっている。どの実装を使っても旧来からの基本部分はきちんと動作するので最低限の利用は問題ないが、それぞれの動作(実装)をときどきチェックしておいたほうが良い。

Webブラウザの拡張機能を使う

 Webブラウザの拡張機能としてPageSpeedを使うには、拡張機能をブラウザにインストールすることとなる。Google ChromeもしくはFirefoxを起動したら、

・PageSpeed Insights Browser Extensions
https://developers.google.com/speed/pagespeed/insights_extensions

にアクセスする。ページ中段にブラウザごとに「Install Chrome Extension」などの緑のボタンがあるので、そこをクリックすると拡張機能がインストールできる。

03-firefox

図3 拡張機能のインストール(Firefoxの拡張機能をインストールしようとしているところ)

 インストール手順は一般の拡張機能と同様で、許諾確認などを行いながら導入して、ブラウザを再起動すればOKだ。

 起動方法は両ブラウザともにデバッグ用のパネル表示から機能タブ選択する。

  • Google Chrome: Ctrl+Shift+Iキーから「PageSpeed」タブで「分析を開始」ボタン
  • Firefox: F12キーから「Page Speed」タブで「Analyze Performance」ボタン

04-chrome-plugin

図4 Google ChromeのPageSpeed

05-firefox-plugin-ext

図5 FirefoxのPageSpeed

 調査したいWebサイト(ページ)を通常のブラウジングのようにブラウザ上表示した状態で、上記を実行すればしばらくして解析結果が表示される。

サイトサービスでPageSpeedを使う

 最も手軽に利用できるのはサイトサービスPageSpeed Insightだ。PageSpeedにおける最新版が提供されている。

・PageSpeed Insights
http://developers.google.com/speed/pagespeed/insights/

06-pagespeed-insight

図6 PageSpeed Insights

 利用するには、上記URLにアクセスし、中央のテキストボックスで調査したいURLを入力して「分析」ボタンを押すだけだ。ここ、さくらのナレッジのトップページを試してみると、図7のようになった(2014年1月上旬)。

07-insight-sakura

図7 PageSpeed Insightsによるさくらのナレッジの調査

 残念ながらPC向けもモバイル向けも低スコアだ。このサイトはWordPressで構築されているが、WordPressをベースとした場合、標準のテーマや関連のテーマそのままのでは、このようなスコアになるのが一般的だ。WordPress最新版の2014年テーマなどでも推奨とされるHTML構造にはなっていないので、高速化するにはカスタマイズが必須となる。

 WordPressに限らず、このような結果はよくあるケースなので、以下では図7の結果にある、

  • ①ブラウザのキャッシュを活用する
  • ②圧縮を有効にする
  • ③スクロールせずに見えるコンテンツのレンダリングブロック JavaScript/CSS を排除する
  • ④画像を最適化する
  • ⑤JavaScript を縮小する・CSS を縮小する

の項目について作業ポイントを紹介していく。

①ブラウザのキャッシュの利用設定

 「ブラウザのキャッシュを活用する」が表示された場合は、WebブラウザでWebサーバーへアクセスする際、その応答にExpireヘッダーなどが欠落しているということである。たとえばDebian標準のApacheパッケージでは、デフォルトでExpireヘッダーは設定されていない。キャッシュ関連のサーバー応答をとくに設定せずに運用していれば、ブラウザに各ファイルがキャッシュされるかどうかはブラウザの実装任せだ。いくつかの環境条件ではページがアクセスされるたびにファイルのダウンロードが発生している。

 各ブラウザにキャッシュを利用してもらうためには、Webサーバー側でファイルの有効期限を明示する。WebサーバーとしてApacheを利用している場合は、Expireモジュールを有効になるように設定し、該当サイトの設定ファイル(/etc/apache2/sites-enabled/*.conf)などでExpiresActiveとExpiresDefaultを設定する。

・Expireモジュールの有効化(Debianでのコマンドの場合)
# a2enmod expires
・Apache設定ファイルにおけるExpireの記述例
ExpiresActive on
<FilesMatch "\.(css|js)$">
	ExpiresDefault "access plus 1 week"
</FilesMatch>
<FilesMatch "\.(gif|jpe?g|png)$">
	ExpiresDefault "access plus 1 month"
</FilesMatch>

 Expireの設定では、ExpiresByTypeディレクティブを使ったMIMEタイプによる設定も可能だが、これには1つ注意点がある。

 <script>タグにおいてはIE対応のために「type=”text/javascript”」を記述するのが一般的だ(RFC的にはapplication/javascriptと書くべきだが、IE8以前で対応していない)。一方HTML5では「type=」の記述は省略可能で、「text/javascript」の記述を省略した<script>タグを利用していた場合は、ExpiresByTypeにおけるtext/javascript指定は機能しないことになる。つまり、<script>タグでの記述と、Webサーバーの設定に整合性を取る必要がある。

 そのため、HTML5のコーディングを考慮しておくと、ExpiresByTypeを使わずに上記のようにFilesMatchで行っておくか、

ExpiresByType text/css "access plus 1 week"

ExpiresByType application/javascript "access plus 1 week"
ExpiresByType text/javascript "access plus 1 week"
ExpiresByType application/x-javascript "access plus 1 week"

のように想定されるMIMEタイプを列挙しておくことになる。

 WebサーバーにNginxを利用している場合は、「expires 1w;」などの記述になる。

・Nginx設定ファイルにおけるExpireの記述例
if ($uri ~* "\.(css|js)$") {
	set $long_expire 1;
}

location xxxx {
	if ($long_expire) {
	expires 1w;
	}
	:
	:
}

 この例では、ファイルの拡張子マッチでExpireの設定が有効になるようにしている。

 なお、キャッシュ関連では直接関係しないが、Nginxのデフォルト設定では.jsファイルのMIMEタイプはapplication/x-javascriptとなっている。

②応答をgzip圧縮する

 「圧縮を有効にする」で指示される内容はいわゆるgzip圧縮によるサーバー応答である。gzipで圧縮された通信を行うには、ApacheならDeflateモジュールを設定していくことになる。

・Deflateモジュールの有効化(Debianでのコマンドの場合)
# a2enmod deflate
・ApacheにおけるDeflateモジュールの設定例
AddOutputFilterByType DEFLATE text/html text/plain text/xml

AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
AddOutputFilterByType DEFLATE application/rss+xml

 Nginxの場合は、標準パッケージに組み込まれているHttpGzipModuleを有効にする。Nginxでは標準設定ファイルでgzipが有効になっているケースが多いと思うが、圧縮対象と設定されているMIMEタイプはデフォルトでtext/htmlのみとなっている点に注意が必要だ。通常はjsやcssの追加設定が必要となる。

・NginxにおけるHttpGzipModuleモジュールの設定例
gzip on;
gzip_vary on;
gzip_disable msie6;
gzip_types text/css application/x-javascript;

 なお、NginxのMIMEタイプ指定はtype記述(Debianの場合は/etc/nginx/mime.typesの設定やソースコード内デフォルト値)に依存する。Nginxではjsファイルの応答を標準でapplication/x-javascriptとしているため、gzip_typesにおいてapplication/x-javascriptによる設定が必要になる。この点はNginxの今後のバージョンで調整される可能性もあるので注意されたい。

③JavaScript/CSSのレンダリングブロック要素を排除

 「スクロールせずに見えるコンテンツのレンダリングブロック JavaScript/CSS を排除する」で指示される内容は最近追加されたルールである(ブラウザ拡張機能では指示されない)。この警告が表示された場合に実施すべきことを端的にまとめると、「CSSは必要なものだけ<head>タグの先頭側にまとめ、JavaScriptは</body>の直前に配置する」を行うことになる。

 「スクロールせずに見えるコンテンツ部分」というのは、「ファーストビュー」と呼ぶことが多い。そのファーストビューさえそれなりの内容で素早く見えていれば、ユーザーからは快適に利用できていると感じてもらえる。とくに回線帯域などのリソースが少ないモバイルデバイスでは違いが出やすく、携帯するデバイスでの閲覧だけに利用者側の要求値も高い。1つ目標として、1秒以内にファーストビューの描画が終わることが求められている。この目標を実現するために、「ファーストビューの描画に関係ないものは、描画が終わってから実行する」を徹底していこうということだ。

JavaScript/CSS設置の基本的な考え方

 Webページの描画において、HTMLソースが上から順番に解析されていき、

  • CSS指定があると、そこで再描画が発生する
  • JavaScriptがあると、その処理が終わるまで一連の処理が停止する

という動きをしており、CSSやJavaScriptは記述方法によってページ表示を遅くする要因となっていることがある。そのため、最低限、

  • 「style=」や<style>タグによるCSS指定は<body>内に設置しない
  • CSS指定は<head>タグの先頭側にまとめて最初に処理する

を実施することでページ描画を高速化できる。<head>タグ内においても処理をブロックしてしまう可能性があるJavaScriptより先頭側にCSSを置くことが基本で、コードの構成イメージを示すと図8のようになる。

08-html-basic

図8 CSS、JavaScriptの順に設置する

 この状態でできるだけJavaScriptの処理を非同期化することがよく行われていたが、それでもコンテンツ描画が遅れることは間違いない。そこで推奨されているのが以下のように</body>の直前にJavaScriptを設置する方法だ。

09-html-basic2

図9 CSS、JavaScriptの順に設置する

 <head>内に記述すること(<body>の前に記述されること)を想定してJavaScriptを構成していた場合は、実行順番などの関係でそのままフッターに移動するときちんと動作しないことも発生しやすい。これからはJavaScriptをフッターに記述する(任意の場所に記述しても動作できる)ことを想定してコーディングすることが必要だ。たとえるなら、ウィンドウプログラミングのようなイベントドリブン的な記述が推奨される。

 以上に加え、そのページに使われているCSSのみを残して使われていないCSS設定を削減し、<head>へのインライン化することも検討される。CSSの設定量とほかのページとの兼ね合いにもよるので一概にはいえないが、CSS設定が少ない場合は<head>タグ内にCSSが直接書かれているのが一番無駄がない。一方でCSS設定でページ共通部分が多く全体量も多い場合は、共通部分のCSSは外部ファイルとしてブラウザにキャッシュしてもらっていたほうが全体として高速化される。

 このあたりは作業コストと実測などをベースに検討することになるだろう。たとえばbootstrapを利用していてサイト特有のCSSを1ファイルにまとめていたとしよう。<head>内は以下のような記述になる。

<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/style.css">

 この状態で各ページで必要なCSSだけを取り出し、<style>タグでインライン化するという作業は現実的ではない。/css/style.cssをサイト全体で使っているのであれば、この2つのファイルを1つにまとめ、YUI Compressorで再圧縮するところまで行えれば、まずは合格としていいだろう。

PageSpeedモジュールによる自動変換も検討

 JavaScriptやCSSの修正は作業コストが大きく、実装状態や管理体制などの理由により、PageSpeedのいうようなアドバイスどおりの作業をあきらめたほうがいい場面もある。そのような場合は、PageSpeedのWebサーバーモジュールによる自動変換が検討候補になる。

・PageSpeed Module
https://developers.google.com/speed/pagespeed/module

pagespeed-module

 Webサーバーの高速化アプライアンスやプロキシサーバーのようなものがこのモジュールで実現でき、すべてのHTTP応答を最適化して配信する。対応するWebサーバーはApacheとNginxだ。これを導入すると、最初にページアクセスが行われたときに、HTMLが解析され、Webサーバーから最適化されたHTML、CSS、JavaScript、画像ファイルが配信できる。一度解析した最適化した配信ファイル群はキャッシュもされる。Webサーバーでの設定作業だけ行えばよいので、難しいことを考えずに最適化できる手軽さがある。

 とはいえ、サイト全体で自動書き換えが発生することになるので、サイト全体の動作をチェックし直すコストも発生してしまう。複雑なサイトではテスト項目は膨大だろうし、モジュールのバージョンが上がり、入れ替えが発生した際のチェックはすべてやり直しになってしまう。CSSやJavaScriptの最適化作業で利用したいソリューションではあるが、リスクと検証のトレードオフを考える必要がある。

 まずは、本番に使うというよりは、テスト環境で実行して自動変換の最適化例を参考にするのがお勧めだ。このモジュールによってどのような修正がなされたか考察すれば、手作業による最適化作業の参考になる。

 また、最適化ルールはカスタマイズできるので、画像配信サーバー的に画像ファイルの圧縮だけ、HTML中の空白文字除去だけといったリスクの少ない最適化のみを実行する使い方も手だ。

テンプレート内のJavaScript実行は配列にpush

 WordPressなどのCMSを使って構築している場合は、ヘッダーテンプレートで関数型定義、特定部品テンプレート内で関数を実行というような形になっていて、すべてをそのままフッターにまとめるということが難しい場合もある。たとえば以下のようなケースだ。

・<head>内
function aaa() {処理}

・中間の部品テンプレートで掲出
aaa();

・フッターテンプレート
</body>

 このような場合は、関数を実行したい部分では配列に実行したい関数をpushしておき、フッター部でまとめて実行する置き換えなら中間テンプレートの構成順番を変えずに記述できるのでわかりやすいだろう。

・<head>内
var myfunc=[];

・中間の部品テンプレートで掲出
myfunc.push(function(){処理});

・フッターテンプレート
<script>
for(var i=0,l=myfunc.length; i<l; i++){myfunc[i]();}
</script>
</body>

 このようにまとめてしまえば、さらにjQueryを使って実行部をラッピングし、遅延処理とすることも簡単だろう。JavaScriptのコーディングは、<head>や<body>部ではテンプレートを構成するような宣言のみに終始し、フッター部でまとめて遅延実行という考え方が基本になる。FacebookやTwitterなどのソーシャルボタンも後から差し込むという感覚で実装していくと良い。

画像ファイルを最適化する

 「画像を最適化する」で示される項目は主にPNGファイルの最適化である。PNGファイルは最小ファイルサイズで保存されているとは限らず、一般的な画像編集ツールだと大き目のファイルサイズになっている。これをoptipngなど最適化ツールで再圧縮すればかなりのファイルサイズを減らすことが可能だ。また、GIFファイルはPNGに置き換えることでファイルサイズが小さくなることが多い。

 なお、「ロスレス圧縮」という言葉が登場するが、これは画像表示に必要なデータを間引くことなく圧縮(縮小)するという意味である。JPEGファイルでは元のデータを変更して縮小することができるので、このデータ劣化に対する劣化なしという意味である。

 PNGファイルを最適化するにはoptipngの最新版がお勧めである。

・optipng
http://optipng.sourceforge.net/

optipng

 Linuxディストリビューションではパッケージとして、公式サイトからはWindowsの実行ファイルもダウンロードできる。

 ファイル最適化は以下のように実行する。

$ optipng -strip all aaa.png
もしくは
$ optipng -o7 -strip all aaa.png

 「-o7」は最適化レベルを指定するものである。数値が大きいほど解析(施行)回数が増え、処理が遅くなる。指定なしの場合は「-o2」と同じである。7を指定すると想定される最小パターンをすべて探すことになるが、通常は「-o2」で十分である。

 注意しておきたいのは「-strip all」オプションである。このオプションはoptipngのバージョン7から追加されたメタデータを削除するもので、これのあるなしでファイルサイズが異なる。PageSpeedでは「-strip all」オプション相当で最適化したファイルサイズで評価するので、このオプションを付け忘れた、もしくは古いバージョンのoptipngを使っていると、「-o7」で最適化していてもファイルサイズが大きくなり、いつまでもPageSpeedに警告されることになる。

 とくにDebianを利用している場合は注意が必要だ。標準パッケージはバージョン0.6系なので、Debianで最適化する場合はソースを入手してビルドした最新版を使う必要がある。簡単にビルドフローを示すと以下のようになる。

# aptitude build-essential
# aptitude build-dep optipng
# wget http://prdownloads.sourceforge.net/optipng/optipng-0.7.4.tar.gz
# tar zxvf optipng-0.7.4.tar.gz
# cd optipng-0.7.4
# ./configure
# make
# make test

# ./src/optipng/optipng -v(実行)

⑤JavaScriptやCSSファイルを縮小する

 「JavaScript を縮小する」、「CSS を縮小する」で指示されるものは、CSSファイルやJSファイルにある余分な空白、冗長な変数・関数名を省略してファイルサイズを小さくできるというものだ。最適化ツールとしてはYUI Compressorがお勧めである。

・YUI Compressor
https://github.com/yui/yuicompressor/releases

yuicomp

 YUI Compressorはjarファイルを実行できる環境を整えて、以下の書式でファイルを圧縮する。

$ java -jar yuicompressor-2.4.8.jar old.css > new.css
もしくは
$ java -jar yuicompressor-2.4.8.jar old.css -o new.css

 YUI Compressorは対象ファイルの拡張子がcssかjsかでCSSかJavaScriptを判定をしているので、拡張子がないファイルを最適化する場合は、「–type css」のように明示的に指定する。複数ファイルをまとめる際、*.cssのような指定も可能だが、記述順番が問題になるケースは多々あるので、catコマンドであらかじめまとめてから実行したほうが良い。また、UTF-8以外でコードを記述している場合は「–charset」で文字コードを指定する。

PageSpeedで最適化手法を身に着けよう

 以上5点のポイントを紹介したが、これだけ実行するだけでもWebサイトのパフォーマンスは改善されるはずだ。実際のところ、CSSやJavaScriptを活用し、ソーシャルボタンなどを配置しているいまどきのWebサイトでは、PageSpeedのアドバイスを完璧に実行し、100点のスコアを出すということは現実的でない場合が多い。まずはモバイルで80点以上、PCで90点以上のスコアを達成できれば合格点としていいだろう。

これまで何も最適化作業を行っていないWebサイトなら、まずはもっとも単純なoptipngによる画像ファイルの圧縮から実践してみることをお勧めする。わかりやすく最適化を実感できるはずだ。そうして実感できたら、ときどきはPageSpeedを利用していけば、自然と快適なWebサイトを構築する術を身に着けていくことができるだろう。