KUSANAGI9(Nginx)で、プラグインEWWW image optimizerを使わないで、WEBPへ自動で変換して画像配信する【2025年12月11日改訂】

Pagespeed Insightsで「次世代フォーマットでの画像の配信」を指摘されました。

改善策はJPGやPNGに代わり、WEBPでの配信。
検索して出てきたのはプラグインEWWW image optimizer使った方法ばかりでした。

しかしもっと調べてみたところ、NginxならプラグインがなくともWEBPの一括変換・自動変換・配信の全てに対応できることがわかりました。
KUSANAGI(Nginx)で実際にやってみたので、その方法について記します。

【2025年12月11日改訂】
・KUSANAGI9のみの対応に変更しました。
・コードミスを修正しました。

アイキャッチの画像はKUSANAGIのイメージキャラクター草薙沙耶 ©PRIME STRATEGY

天満川鈴 WRITTEN BY 天満川鈴

はじめに KUSANAGIを個人利用している方へ【2025年12月11日追記】

KUSANAGIを個人利用している方でメモリ4G以上の契約ならWEXALの導入を勧めます。
本稿の内容は全てWEXALが自動でやってくれます。
個人利用であればWEXALは無料です。

私も本稿のセッティングからしばらくWEXALに変更していました。
しかしここ最近は、メモリ2Gだとサーバーダウンが頻発するようになってしまいます。
やむなく本稿の設定に戻しました。

本稿の主旨

本稿では、KUSANAGI(Nginx)でWEBPの変換・配信する方法を説明します。
変換については既存のファイルをwebpに一括で変換し、なおかつ加わったファイルも自動でwebpに変換します。

EWWW image optimizerを使った方法なら幾らでも出てきます。

しかし本稿の方法ですと、プラグインは一切必要ありません

「KUSANAGI WEBP」で検索してみたら「EWWW image optimizerを使って」ばかりでした。
しかし一方で「Nginxならプラグインなくても簡単にできる」とも出てくる。
「この差はどうして?」と思ったので、実際にKUSANAGIでWEBP画像の変換&配信をEWWW image optimizer無しで導入してみた次第です。

本稿の方法におけるメリットは、

  1. WEBP変換プラグインを1つ減らせること
  2. WEBP変換を画像登録より後で行うので登録時の重さが解消できること

ただし2はWEBPが即座に反映されない点でデメリットでもあります。
30分ほどのタイムラグをどう捉えるかですね。

始める前に KUSANAGIの画像最適化機能について

KUSANAGIの画像最適化機能は、他のWordPressプラグインと比べて、全く劣りません。
検証した記事がこちら。

jpgについては完全に同等。
pngについては可逆圧縮か非可逆圧縮の差じゃないかと思います。
私はこれで足りるので他の画像最適化系プラグインは使っていません。

KUSANAGIで既存の画像ファイルを一括でWEBPに変換する

こちらの記事を参考にして進めます。

1 cwebpをインストールする

KUSANAGIにはデフォルトで入ってないのでインストールします。
(もし入っていて、私が気づかなかっただけならごめんなさい)

次のページにアクセスします。

私は「libwebp-1.1.0-rc2-linux- x86-64.tar.gz」をダウンロードしました。
これで動いたので参考にしてください。

ローカルで解凍して、binフォルダに入っているファイル群を「/usr/local/bin」にアップします。

gzip対応の解凍ソフトは予め御用意ください。
私はLhaForgeを使っています。

2 シェルスクリプトを作成する

ローカルで新規テキストファイルを作成します。
ファイルの中身は、次のように書きます。

#!/bin/bash
DIR="/home/kusanagi/【プロファイル名】/DocumentRoot/wp-content/uploads/20??/*" # 対象ディレクトリパス(要変更)
JPEG_CWEBP_OPTS="-q 75 -m 4" # Jpeg向け非可逆cwebpオプション
PNG_CWEBP_OPTS="-q 75 -m 4" # PNG向け非可逆cwebpオプション
CWEBP="/usr/local/bin/cwebp" # cwebpコマンドをインストールした場所、1の通りにしたならこのままで

cd "$(dirname "$0")" # パスにスペースが含まれる可能性を考慮し、ダブルクォートで囲むのが安全
shopt -s nocasematch

find $DIR -type f -regextype posix-extended -iregex ".*\.(jpe?g|png)$" -print0 | \
while IFS= read -r -d '' SRC; do
WEBP="$SRC.webp"
if [[ ! -e "$WEBP" || "$SRC" -nt "$WEBP" ]]; then # 変数の展開時にエラーを防ぐため、ダブルクォートで囲むのが望ましい
if [[ "$SRC" =~ \.jpe?g$ ]]; then # 変数の展開時にエラーを防ぐため、ダブルクォートで囲むのが望ましい
echo "Convert to lossy WebP: $SRC"
"$CWEBP" $JPEG_CWEBP_OPTS "$SRC" -o "$WEBP"
elif [[ "$SRC" =~ \.png$ ]]; then # 変数の展開時にエラーを防ぐため、ダブルクォートで囲むのが望ましい
echo "Convert to lossless WebP: $SRC"
"$CWEBP" $PNG_CWEBP_OPTS "$SRC" -o "$WEBP"
fi
fi
done

「webp.sh」という名前で保存します。

画像ファイルは年月でフォルダわけをしている場合を念頭においてます。

「20??」の「?」は「任意の1文字」

この場合は「2020など頭に20がついたフォルダ」を指します。

「*」は「任意の文字数の任意の文字」。

この場合は「2019や2020フォルダの直下にある全てのフォルダ」となります。

この点は絶対に注意してください。

①UTF-8にすること ②改行方式は「LF」にすること

②は見落としがちです。
もし忘れると、

WEBPの自動変換ができない!
シェルスクリプト動かそうとしても「webp.shなんてファイルはない」って怒られる!

これで数時間無駄にしました。

その他のオプションについてはこちらを御覧ください。

3 webp.shをアップロードする

プロファイル直下にshフォルダを作って、その中に入れます。

/home/kusanagi/【プロファイル名】/sh

アップしたら実行権を与えます。
黒い画面で次のコマンドを打ち込みます。

# chmod a+x /(入れたパス)/webp.sh

4 既存の画像ファイルをWEBPへ一括変換する

ログインして、黒い画面に次の通り入力します。

# cd /home/kusanagi/【プロファイル名】/sh
# ./webp.sh

うまくいっていれば、すごい勢いで変換が始まります。
終わるまでには少々時間が掛かります。
終わったらフォルダを開いてみてください。
webpファイルができあがっているはずです。

webpファイルの名前は「~.jpg.webp」や「~png.webp」など「元の拡張子+webp」となります

なお、この作業は、シェルスクリプトの動作確認も兼ねています。

KUSANAGIで追加した画像ファイルをWEBPに自動変換する

webp.shをcronに登録して自動実行させます。

KUSANAGI8の場合は/etc/crontab。
KUSANAGI9の場合は/var/spool/cron/root
などを開きます。
(「など」というのは複数設定ファイルが複数あるのでどれでもいいという意味です)

*/30 * * * * (ユーザー名) /home/kusanagi/【プロファイル名】/sh/webp.sh >>/home/(自分のWordPress環境のパス)/uploads/webp.log 2>&1

保存して、

# kusanagi restart

webp.shは自分の入れたパス名に変更してください。
私と同じならこのままでOKです。

ユーザー名は、例えばkusanagiなど。

30は「30分ごとに自動実行する」設定です。
最初は10分程度に設定してみて、10分経過したらuploadsフォルダにログファイルがあるか確認してください。
さらに中身をチェックしてみてください。
正常なログが吐き出されていれば自動実行されています。

もしログファイルがなければ、あるいはおかしければ、cronの動作のログをチェックします。
/var/log/cronを開いてみてください。
シェルスクリプトが動作しない手掛かりが記されているはずです。

KUSANAGIでwebpを自動配信する

これらの記事を参考に進めました。
両方読んだ方がわかりやすいと思います。

次のファイルを開きます。

/etc/opt/kusanagi/nginx/conf.d/(プロファイル名).conf

一番上に、次の記述をします。

map $http_accept $webp_suffix {
# Acceptヘッダーに 'image/webp' が含まれていれば '.webp' を設定
# "~*" は大文字小文字を区別しない正規表現マッチングを指定
~*image/webp ".webp"; 

# 上記の条件に一致しない場合のデフォルト値は空文字列
default "";
}
server{}の外に記述してください。

SSL側(2つある内の下側)のserver{}内の「error_log(略)」と書かれた行と「 charset UTF-8;」と書かれた行の間に次の記述します。

location ~* \.(png|jpe?g)$ {
# 1. 有効期限をしっかりセット(None対策)
expires $expire_days;

# 2. Cache-Controlを明示的に追加(継承が切れるのでここで書く)
add_header Cache-Control "public, max-age=7776000"; # 90日
add_header Vary Accept;

# WebPがあればそれを、なければオリジナルを
try_files $uri$webp_suffix $uri.webp $uri =404;
}

保存して、

# kusanagi restart

実際にwebp配信がされているかチェックします。
Chromeでサイトを開いてF12でデベロッパーツールを起動→ctrl+F5キーで更新します。

変換前

赤で囲んだ画像が新しくメディアライブラリに入れた画像です。

30分後。

Typeが「webp」に変わっていればOKです

一見するとjpgで配信されているかに見えます。

しかしwebpで配信されていることはサイズでわかります。
78.9kb→31.7kbと半分以下になっています。

ファイル名が同じなのは「コンテンツを変えずに配信するファイルだけを変える」からです

この例ですと、webp対応のブラウザなら「conoha3m1.jpg.webp」を配信、そうでなければ「conoha3m1.jpg」を配信。だけどコンテンツは「conoha3m1.jpg」のままとなります。

一言で「仕様」。
この点がEWWWを使った場合とは異なります。
EWWWを使った場合は、コンテンツも「hoge.jpg.webp」に変わっています。

まとめ

本稿を書いた時は、まだWEXALがありませんでした。
なので現在でしたら(サーバースペックに余裕があるなら)WEXAL導入を勧めます。

ただNginxだけどKUSANAGIじゃなかったり。
法人利用だったり。
そういった方には、ぜひ実装をおすすめします。

元の紹介記事を初心者が読んでやろうとすれば、まずどこかで躓きます
私も躓きました。

だけど本稿を読みながらならスムーズにできるはず!

頑張ってチャレンジしてみてください!

天満川 鈴のプロフィール画像
WRITTEN BY

天満川 鈴

未経験からWEB業界に入り、現在はWEBディレクターとして実務に従事。 要件整理・導線設計・コンテンツ構成などを学びながら、日々改善を重ねています。 AIを活用したコンテンツ制作・効率化を強みとし、プロンプト設計を含めた制作フローの最適化にも取り組んでいます。
本サイトでは、WordPressやサイト制作に関する試行錯誤・検証内容を中心に発信。 技術検証の一環として、KUSANAGI公式サイトにて記事を2回紹介いただきました。

RECOMMENDED INFRASTRUCTURE

私はConoHa以外を勧めない。

2016年からずっとConoHaを使い倒してきました。知人に「一番いいサーバーは?」と聞かれたら、迷わずここを教えます。

レンタルサーバーナンバーワンを誇る高速環境であることはもちろん。私が「黒い画面って何?」というド素人からサイト制作のプロになれたのは、傍らにずっとこのはちゃんがいてくれたから。

私がConoHaを使い続ける、嘘偽りない理由です。

公式サイトで詳細を見る
× 閉じる