nginxでJavaScriptのminifyを実現する

はじめに

 JavaScriptは、転送量を抑えるために利用できる技術として、「minify」が挙げられます。

 通常はタスクランナー等で処理を行ってからデプロイするわけですが、これをnginx側でやらせることによって、デプロイ工数の削減が期待できます。

 公式ページのEmbedded Perl Minify JSによると、perlプラグインを利用することで、で比較的簡単に実現できそうです。

nginxのperlモジュールを有効化する

 perlは、例えばaptやyumなどでインストールを行った場合、デフォルトで有効になっているとは限りません。まずこれらを有効にする必要があります。

yumの場合

 CenoOS系に代表されるyumの場合、/usr/share/nginx/modulesあたりにconfファイルが存在するはずですが、ない場合はモジュールを追加します。

モジュールのインストール

$ yum install nginx-mod-http-perl

 または、

$ yum install nginx-all-modules

perlモジュールの読み込み

 以下のコードをnginx.conf(または準ずるファイル)のトップレベルに記述します。

include /usr/share/nginx/modules/mod-http-perl.conf

aptの場合

 debian系のaptの場合は、libnginx-mod-http-perlパッケージをインストールする必要があります。

モジュールのインストール

$ sudo apt install libnginx-mod-http-perl

perlモジュールの読み込み

 /etc/nginx/modules-enabledに50-mod-http-perl.confがあればOKです。

 なければ、modules-availableからシンボリックリンクを貼りましょう。/etc/nginx/modules-availableか、もしくは/usr/share/nginx/modules-available/にモジュールがあるかと思います。

 いずれにもない場合は、findなどで検索するとよいでしょう。

$ sudo ln -s /usr/share/nginx/modules-available/50-mod-http-perl.conf /etc/nginx/modules-enabled

JavaScript Minifierを配置する

 CPANでJavaScript::Minifierをインストールします。

> install JavaScript::Minifier

 モジュール自体は、CPANからダウンロードして@INCのパスが通っているところに配置することも出来ます。

 @INCのパスは、以下のコマンドで確認できます。

$ perl -e 'use Data::Dumper;print Dumper @INC'

Minify.pmを配置する

 公式のperl/Minify.pmを参考にして、同じく@INCのパスが通っているところに配置します。

 もしperlが書けるなら、これを編集することでより柔軟性のあるスクリプトを組むことができます。参考として、変更できそうなところをコメントしておきます。

package Minify;
use nginx;
use JavaScript::Minifier qw(minify);

sub handler {
  my $r=shift;
  my $cache_dir="/tmp";  # Cache directory where minified files will be kept # キャッシュディレクトリです。デフォルトは/tmpに配置するようになっています。
  my $cache_file=$r->uri;  # 引数オブジェクト内のuriをそのままキャッシュファイル名とします。Cache Bustingを行うならquery_stringも付加するとよいでしょう。
  $cache_file=~s!/!_!g;  # キャッシュファイル名に/があれば、_に置換しています。
  $cache_file=join("/", $cache_dir, $cache_file);
  my $uri=$r->uri;
  my $filename=$r->filename;

  return DECLINED unless -f $filename;

  if (! -f $cache_file) {
    open(INFILE, $filename) or die "Error reading file: $!";
    open(OUTFILE, '>' . $cache_file ) or die "Error writting file: $!";
    minify(input => *INFILE, outfile => *OUTFILE);
    close(INFILE);
    close(OUTFILE);
  }
  $r->send_http_header("application/javascript");
  $r->sendfile($cache_file);
  return OK;
}
1;
__END__

 参考:Module ngx_http_perl_module

nginx設定を変更する

 準備が完了したら、nginxの設定を変更します。

 serverディレクティブ以下に下記を追記します。

location ~ \.js$  {
  perl Minify::handler;
}
本設定は.jsで終わる全てのurlに対してminifyを実行します。

 nginxをリロードします。

sudo systemctl reload nginx

注意点

 このモジュールを使うときに注意すべき点がいくつかありますので、挙げておきます。

初回のアクセスは遅い

 リクエストを受けたときに初めてMinifyが実行されます。2回目以降はキャッシュが作られるので問題ありませんが、初回導入時は気を付ける必要があります。

 デプロイ後は、すべてのリソースに一度はアクセスしておくのがよいでしょう。

JavaScriptを更新したときにキャッシュの削除が必要

 キャッシュがあると、必ずそちらを使うようになっています。

 Minify.pmでファイルのタイムスタンプを比較するという手段もありますが、速度が犠牲になるため一長一短です。

/tmpをキャッシュディレクトリにしている場合、一定のタイミングでキャッシュが削除される

 意図しないタイミングでキャッシュ削除が起こり、レスポンスが一時的に劣化する可能性があります。

 保持しておきたい場合は、キャッシュディレクトリを変更しておきましょう。

my $cache_dir="/path/to/cachedir";

 参考:/tmp/ディレクトリ下のファイルはいつ削除される?

まとめ

 この手順を適用することで、毎回Minify作業を実施することなく、自動的にMinifyが掛かってくれるようになります。

 タスクランナーでMinifyを自動化してデプロイするほうが主流ですが、こういうやり方もあるということで、参考程度にしていただけると幸いです。