こんにちは、WordPress高速化ネタが大好きなあんそくです。

WordPressを使って運営しているGeeklesで行なった、nginxのモジュール「ngx_small_light」による動的なサムネイル画像のリサイズについてまとめます。

Webページを構成する要素の中で、一番容量が大きい画像ファイル。これを動的に適切なサイズでリサイズすることができるようになったので、ページが非常に軽量化しました。

ngx_small_lightとは

ngx_small_lightとは、サーバー側で簡単に任意のサイズに画像のリサイズが行えるようになるnginxモジュール。リサイズだけなく、切り抜きや合成、png→jpgへのタイプ変換なども行えます。

Apacheの「mod_small_light」がNginx向けに移植されたもので、元画像URLにパラメータを付けてアクセスするだけで、簡単に画像を変換することができます。

記事型メディアサイトのテンプレートでは、最新記事や関連記事、カテゴリページなど様々な箇所でアイキャッチ画像を呼び出します。しかし、デザインの都合やPC/スマホなど条件によっては同じ画像でも、表示するサイズ(解像度)が異なる箇所が多く存在します。

そんなとき、ngx_small_lightを使って動的に適切な画像を返せるようになると、訪問者に無駄に大きな解像度の画像をダウンロードさせなくて済むので、ページの容量が減り、訪問者の体感速度は上がります。

もちろん、最適なサイズの画像を予めたくさん用意しておくという手もありますが、バックアップやテンプレート変更時の管理コストが大変ですよね……。

同じ画像を様々なサイズで表示する

動的にリサイズして適切なサイズの画像を返すメリット

  • 変換方法や画質などが簡単に設定・変更できる
  • 無駄にファイルが作られない
  • テンプレートリニューアル時の対応が楽

あらかじめ全ての適切なサイズを用意しておくことのデメリット

  • サーバーの容量を食う
  • ファイル数が大量になると、バックアップなどファイル操作が大変
  • テンプレートリニューアル時に全て生成し直す必要あり
  • Post Thumbnail Editorを使っている場合、メディアの数だけ対応が増えてしまうので大変

ということを考えて、元々はPC向けに生成した少し大きめな画像をスマホ版にも出していて、無駄に訪問者の体感速度を下げていたGeeklesでは、ngx_small_lightを導入することにしました。

ngx_small_lightができること

以下の画像を例に各種パラメータの例を紹介します。

http://geekles.net/images/uploads/2016/03/23222814.png

http://geekles.net/small_light(dw=300,dh=212)/images/uploads/2016/03/23222814.png

元画像URL対して、small_light(dw=300,dh=212)/を付けたアドレスにアクセスするだけで、横300px,縦212pxにリサイズされた画像が返されます。

http://geekles.net/small_light(dw=100)/images/uploads/2016/03/23222814.png

横幅100pxにリサイズ。

http://geekles.net/small_light(dh=100)/images/uploads/2016/03/23222814.png

高さ100pxにリサイズ。

といったように、横幅や高さだけを指定すれば、指定していない方もアスペクト比がそのままになるようにリサイズされます。

リサイズ以外にも使える

画像形式の変換

出力形式をJPG、PNG、GIF、WebPから選択することができます。PNGからJPGへ変換するだけで容量が減ったりもするので、便利ですね。

画質の指定

http://geekles.net/small_light(of=jpg,q=5)/images/uploads/2016/03/23222814.png

わかりやすくするために、画質の設定を5/100とかなり落としたので凄いことになってますが、簡単に容量を減らすこともできちゃいます。

切り抜き

http://geekles.net/small_light(cw=100,ch=100)/images/uploads/2016/03/23222814.png

キャンバスサイズを指定することで、好きなサイズに切り抜きも可能。「ここだけアスペクト比が微妙に違う」なんて見せ方をしたいパーツのときに重宝します。

他にも色々なパラメータがあり、画像の合成や回転なども行えます。

とにかくめちゃくちゃ便利なのです!!

ngx_small_lightの導入方法

導入方法ですが、Nginxをビルドする際にngx_small_lightモジュールも一緒にビルドして、インストールするだけ。

私のような非エンジニアだと、少し難しくも感じますが、実際にやってみるとそこまで難しい作業ではありません。VPSをセットアップして、WordPressのサイトを構築できているのであれば、その延長です。

インストールが完了したら、Nginxのconfファイルに下記を追加します。

small_light on;
location ~ small_light[^/]*/(.+)$ {
set $file $1;
rewrite ^ /$file;
}

これで、冒頭で紹介したように、small_light(dw=100)のようなリクエストを投げると、サーバー側で画像をリアルタイム変換して返してくれるようになりました。

WordPressでngx_small_lightが返す画像を扱えるようにする

WordPressでngx_small_lightを使えるようにするには、大きく分けて3つやることがあります。

  1. Nginxのconfファイル側の設定
  2. function.phpにリサイズ画像を取得できる関数を定義
  3. テンプレートファイル側でリサイズ画像を書き出すように設定

1.Nginxのconfファイル側の設定

どのようなURLにリクエストを投げて、画像を変換するかの定義を行います。

# ngx_small_light
small_light on;
location ~ ^/images/uploads/resize/(\d+)x(\d+)/(.+)$ {
proxy_cache        image_cache;
proxy_cache_valid  200 720m;
proxy_cache_key    $scheme://$host$request_uri;
proxy_pass         $scheme://127.0.0.1/small_light(dw=$1,dh=$2,q=95,jpeghint=y)/images/uploads/$3;
}
location ~ ^/small_light[^/]*/(.+)$ {
set $file $1;
rewrite ^ /$file;
}

※WordPressの画像フォルダの設定によって、proxy_passの設定などが変わるかと思います。

この設定をすることで、
http://geekles.net/images/uploads/resize/100×100/2016/03/23222814.png
のような架空のURLへのアクセスが、サーバーの内部的に
http://geekles.net/small_light(dw=100,dh=100,q=95,jpeghint=y)/images/uploads/2016/03/23222814.png
へのリクエストとして処理されるようになります。

2.function.phpでの設定

次はWordPressのテンプレートで、リサイズ画像へのリクエストができるように、function.phpに関数の定義をします。

function the_resize_thumbnail($post_id, $image_size, $width, $height) {
$thumbnail_id  = get_post_thumbnail_id($post_id); 
$thumbnail_img = wp_get_attachment_image_src($thumbnail_id, $image_size);
$thumbnail_url = $thumbnail_img[0];
$resize_thumbnail_url = str_replace("/images/uploads/", "/images/uploads/resize/". $width ."x". $height ."/", $thumbnail_url);
return $resize_thumbnail_url;
}

※function.phpは編集に失敗すると、サイトへアクセスができなくなってしまうので、慎重に!

これで、the_resize_thumbnail関数がテンプレート内で呼び出せるようになりました。

3.テンプレートファイルの設定

一般的にWordPressのテンプレート内でアイキャッチ画像を呼び出すには、このような指定をしているかと思います。

<?php the_post_thumbnail('post-thumbnails'); ?>

これを先ほど定義した、the_resize_thumbnail関数を使ったものに変更します。

<img src="<?php echo the_resize_thumbnail($post->ID, 'thumbnail', 192, 107); ?>" width="192" height="107" />

the_resize_thumbnail関数は画像のURLしか返さないので、imgタグなどは予め書いておきます。

the_resize_thumbnail関数には、メディアの名前、横幅、高さを引数として設定できるようにしました。これにより、呼び出す箇所によって、別々のサイズの画像を動的に返すことができるようになっています。

比較

それでは、画像ファイルの容量はどの程度軽量化できるのか見てみましょう。

Geeklesのスマートフォン版では、関連記事のアイキャッチ画像をPC向けに生成しているものと共通で使っていました。大きいサイズの画像をwidth,height設定で小さく表示させていたので、無駄に大きな画像を使っていたということになります。

59.6KB

まずは、これまで使用していたPC向けサムネイルをそのまま呼び出したファイル。

25.9KB

PCで使う用に画質だけ落とすために、q=95という指定をしたファイル。
このパラメータを付けるだけで、解像度はそのままで、見た目もほぼ変化無しなのに容量が半分以下に減りました。

19.6KB

これがGeeklesスマホ版の関連記事などに使っているアイキャッチ画像。

画像によって圧縮率は変わりますが、約60KBから約20KBと1/3程度に圧縮できました。関連記事は何件も表示させることが多いので、画像1個あたりの圧縮できる容量は少なくても、結構なインパクトに。

ページ容量の比較

実際にページ全体の容量がどの程度変わるのかテストしてみました。

テストしたのはこのページのスマホ版。なお、Geeklesでは記事内の画像をLazy Loadしているので、今回のテストには無関係になっています。記事の下にある関連記事のアイキャッチ部分での比較となります。

ngx_small_light適用前

ページの容量は3.5MB。ページレンダリング完了までに1.17秒かかっています。個別のファイルを見てみると、数百キロバイトの画像ファイルがたくさんあることがわかります。

ngx_small_light適用後

ページの容量は952KBと大幅減、ページレンダリング完了も1.06秒わずかですが短縮できました。たくさんあった数百キロバイトの画像は1つを残すだけで、すべて数十キロバイトになっています。

まとめ

ngx_small_lightをWordPressで使う簡単な方法について紹介しました。

とりあえず関連記事のアイキャッチ部分で利用しただけなので、記事内の画像にも適用していけば、ページ容量をさらに圧縮できるかと思います。

ページが高速に表示されることはSEOの観点からも良しとされているので、こういった面でのページ高速化、WordPress高速化も検討してみてはいかがでしょうか。