AWS版Movable TypeでカジュアルにWebP対応するレシピ

[Movable Type Advent Calendar 2021 \- Adventar](https://adventar.org/calendars/6369) の記事投稿です。

12月18日に及川さんがこちらのプラグインを紹介していましたが、

[WebP画像を自動生成する「WebPfy for Movable Type」 \| 株式会社cherry\-pick](https://www.cherry-pick.jp/blog/contents/webpfy-for-movable-type.php)

この記事ではAWS版Movable Type限定ですが、10分程度で全自動WebP対応を実現するレシピを紹介します。

* **無料** Amazon Linux 2の機能のみにで実現できます。
* **従来フォーマットにも自動対応** ブラウザを判別してWebPか従来フォーマットか選択します。iOS13以下のiPhoneや旧ブラウザでも安心です。

# 作業手順

## 1. AWS版Movable Typeのセットアップ

こちらの記事などご参考ください。今回は **nginxタイプを使用** しました。

[Movable Type AMI版（AMI）スタートガイド\| CMS プラットフォーム Movable Type を Amazon EC2 のクラウドで](https://www.sixapart.jp/movabletype/aws/how-to-use.html)

とりあえずテスト用の画像ファイルをアップロードしておいてください。

## 2. webp-toolsのインストール

EC2インスタンスにSSHログインして、次のコマンドを実行します。

    sudo yum install webp-tools

WebP変換のコマンドラインツールをインストールされます。

## 3. WebP変換バッチ

パスはどこでもよいので、次のシェルスクリプトを作成します。ここでは `/home/ec2-user/webp.sh` としました。

```bash
#!/bin/bash

DIR="/data/file/static" # 対象ディレクトリパス(要変更)
SUDO="sudo -u www"
CWEBP="/usr/bin/cwebp" # cwebpコマンドの場所
JPEG_CWEBP_OPTS="-q 75 -metadata icc" # Jpeg向け非可逆cwebpオプション
PNG_CWEBP_OPTS="-lossless" # PNG向け可逆cwebpオプション
GIF2WEBP="/usr/bin/gif2webp" # gif2webpコマンドのパス
GIF2WEBP_OPTS="" # GIF向けgif2webpオプション

cd $(dirname $0)
shopt -s nocasematch

find $DIR -type f -regextype posix-extended -iregex ".*\.(jpe?g|png|gif)$" -print0 | \
while IFS= read -r -d '' SRC; do
  WEBP="$SRC.webp"
  if [[ ! -e $WEBP || $SRC -nt $WEBP ]]; then
    echo "Convert $SRC to $WEBP"
    if [[ $SRC =~ \.jpe?g$ ]]; then
      $SUDO "$CWEBP" $JPEG_CWEBP_OPTS "$SRC" -o "$WEBP" 2>&1
    elif [[ $SRC =~ \.png$ ]]; then
      $SUDO "$CWEBP" $PNG_CWEBP_OPTS "$SRC" -o "$WEBP" 2>&1
    elif [[ $SRC =~ \.gif$ ]]; then
      $SUDO "$GIF2WEBP" $GIF2WEBP_OPTS "$SRC" -o "$WEBP" 2>&1
    fi
    echo 
  fi
done
```

このバッチファイルに実行可能なファイルパーミッションを与え、一度実行してみます。

    chmod +x /home/ec2-user/webp.sh
    /home/ec2-user/webp.sh

このバッチは、ドキュメントルートにあたる `/data/file/static` 以下の画像ファイル(拡張子`.jpg`、`.jpeg`、`.png`、`.gif`)をWebPに変換します。

例えば、 `/data/file/static/image.jpg` は `/data/file/static/image.jpg.webp` に変換するので、元のファイルも残ります(ブラウザに応じた出し分けは後ほど実装します)。

なんだか変換されたようなログが出力されたら成功です。

```txt
Convert /data/file/static/first/jpeg.jpg to /data/file/static/first/jpeg.jpg.webp
Saving file '/data/file/static/first/jpeg.jpg.webp'
File:      /data/file/static/first/jpeg.jpg
Dimension: 240 x 214
Output:    3822 bytes Y-U-V-All-PSNR 41.38 44.12 41.57   41.77 dB
block count:  intra4: 113
              intra16: 97  (-> 46.19%)
              skipped block: 68 (32.38%)
bytes used:  header:             66  (1.7%)
             mode-partition:    623  (16.3%)
 Residuals bytes  |segment 1|segment 2|segment 3|segment 4|  total
    macroblocks:  |       2%|      26%|      32%|      38%|     210
      quantizer:  |      36 |      32 |      27 |      19 |
   filter level:  |      16 |      10 |       7 |       4 |
```

## 4. 出し分けの設定

nginx の設定ファイルを少しだけ変更します。

### /etc/nginx/nginx.conf

40行目付近の、

```nginx
    server {
        listen              80;
        include             /etc/nginx/nginx_common.conf;
    }
```

この上に次のように設定を追記します。

```nginx
    # ここを追加
    map $http_accept $webp_suffix {
        default "";
        "~*image/webp" ".webp";
    }

    server {
        listen              80;
        include             /etc/nginx/nginx_common.conf;
    }
```

### /etc/nginx/nginx_common.conf

33行目付近の、

```nginx
location / {
    ssi     on;

    perl    MTDynamicHandler::handler;

    root    /data/file/static;
    limit_req zone=one burst=100;
}
```

の内部に次のように設定を追記します。

```nginx
location / {
    ssi     on;

    perl    MTDynamicHandler::handler;

    root    /data/file/static;
    limit_req zone=one burst=100;

    # ここを追記
    location ~* \.(png|jpe?g|gif)$ {
	add_header Vary Accept;
        try_files $uri$webp_suffix $uri =404;
    }
}
```

設定を変更したら nginx を再起動します。

    sudo systemctl restart nginx

## 5. 動作確認

ブラウザで開発者ツールを開き、画像が含まれるページで`Network`タブを確認します。

![テスト_-_First_Site.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35178/eaf8a578-f215-bce0-cffd-3843d493bfc1.png)


画像の`Type`が`webp`になっていれば、WebP対応は完了です。

## 6. 定期処理を登録

WebPへの変換バッチが定期的に実行されるよう、cronに登録します。

    crontab -e

例えば次のように記述すると、WebP変換バッチが10分ごとに実行されます。

    */10 * * * * /home/ec2-user/webp.sh

# 注意点など

以上で画像をアップロードすると、それが10分以内にWebPに自動的に変換され、軽いファイルとして配信される仕組みができました。

お手軽な分、次のような注意点もあります。

* **WebPの方がファイルが大きくなることも** WebPは従来フォーマットより軽いことを保証するものではありません。特にGIFはWebPの方がファイルサイズが大きくなってしまうことがたまにあります。
* **非対応のファイルや拡張子ねじれ** WebP自体、CMYKカラースペースには非対応ですし、拡張子`.png`なのに内容がJPEGといった変なファイルもたまにあります。
* **同じファイル名で上書きするとタイムラグが発生** 更新日が変わると再度WebPへの変換が行われますが、定期処理が行われるまでWebPファイルは古い内容のままです。
* **バッチの排他制御** もし大量のファイルが追加および更新され、10分で処理しきれない場合は大変なことになるかもしれません。

などなど、サイトによっては運用の品質に関わるところもあるでしょう。

有料のソリューションはこのあたり対処されていると思われます(少なくとも弊社では対応済み)。