※ nginx向けの設定をこちらに書きました。
Webサーバー上のJpegやPNGファイルについて、そのWebP版ファイルがある場合はそちらを返す(もちろんブラウザが対応している場合のみ)、という.htaccess
の記述例はすでにいろいろ公開されています。
多少の差違はありますが、たいてい以下のような方法をとっています。
※ 2018/10/31追記 Varyヘッダの送出条件に関するプルリクがマージされました。混乱するので、以前のコミットにリンク先を変更します。
コメントを日本語に置き換えて説明します。
<IfModule mod_rewrite.c>
# Rewriteモジュールを有効にする
RewriteEngine On
# ブラウザから送信されるAcceptリクエストヘッダがimage/webpを含む場合のみ
# 後続のRewriteRuleを適用する
RewriteCond %{HTTP_ACCEPT} image/webp
# WebP版のファイルがある場合のみ後続のRewriteRuleを適用する
RewriteCond %{DOCUMENT_ROOT}/$1.webp -f
# *.jpg、*.pngファイルを*.webpファイルに内部的にルーティングする
# Content-Typeはimage/webpにして環境変数acceptを1にする
RewriteRule (.+)\.(jpe?g|png)$ $1.webp [T=image/webp,E=accept:1]
</IfModule>
<IfModule mod_headers.c>
# 環境変数acceptが真の(上記RewriteRuleで1が設定された)場合、
# CDNやキャッシュサーバーがWebPとJpeg/PNGをそれぞれキャッシュできるように
# VaryレスポンスヘッダにAcceptを追加する
Header append Vary Accept env=REDIRECT_accept
</IfModule>
<IfModule mod_mime.c>
# 拡張子.webpファイルはContent-Typeとしてimage/webpを返す
AddType image/webp .webp
</IfModule>
WordPressのEWWW Image Optimizerプラグインが提示する方法なのでメジャー感がありますが、改善案を示してみたいと思います。
改善案について
WebP版ファイルの命名規則の改善提案
個人的には元のファイルの拡張子も含めた上で、.webp
を追加する方がよい気がします(例: image.jpg → image.jpg.webp)。
- 将来WebPを直接出力するケースも増えるのではないか。そのとき、変換によって生成されたファイルなのか(image.jpg.webp)、オリジナルとして出力したWebPなのか(image.webp)区別をつけやすい。
- ファイル名が重複したとき(logo.pngとlogo.jpgなど)、衝突を避けられる。
無意味な心配や、拡張子が続くのが嫌といった意見もあるかと思います。
.htaccessの配置箇所が固定される
メジャー版の.htaccessファイルは、DocumentRootに設置する前提になっています。例えば/images
ディレクトリに設置すると、1箇所書き換える必要があります。
RewriteCondの工夫でできないかなーと思いましたが、拡張子置き換えの場合は無理そうでした(アイデアがあればコメントでぜひ!)。
# WebP版のファイルがある場合のみ後続のRewriteRuleを適用する
# サブディレクトリの場合はそのパスが必要
RewriteCond %{DOCUMENT_ROOT}/images/$1.webp -f
WebPに置換したときのみVaryヘッダにAcceptを返している
※ Webサーバから直接画像を配信している場合は影響ありません。
これがちょっとマズくない?と思ったところで、ChromeなどからのアクセスによりRewriteRule
が適用されてWebP版のファイルに置き換えられたときのみ、レスポンスヘッダにVary: Accept
が含まれます。
Firefoxからのアクセス時はVary: Accept
が含まれません。
これって、運悪く最初のリクエストがFirefoxからあってVary: Accept
なしでJpegファイルを返すと、その後ChromeがアクセスしてもキャッシュヒットしちゃってWebPを返さないんじゃない?と思いました。
本来は**.jpgや.pngへのアクセス全体にVary
ヘッダを返す**という実装の方が望ましいのではないでしょうか。そんなことないよ!という場合はご指摘ください。
CloudFrontの場合
試してみましたが、FirefoxにVary
を返さないメジャーな方法でも問題ありませんでした。先にFirefoxでアクセスしてその後ChromeでアクセスしてもJpeg、WebPがそれぞれ返されました。
CloudFrontでは、リクエストヘッダのホワイトリストにAccept
を追加しなければなりませんが(指定しないとそもそもオリジンにリレーされないので)、その作用かもしれません。
以上を踏まえた改善案
メジャー版に対する変更点・改善点は以下の通りです。
-
変更点
image.jpg→image.webp ではなく image.jpg→image.jpg.webpを前提とします。- この変更があるのでEWWW Image Optimizerとはそのまま併用できません。
-
改善点
DocumentRoot以外の任意のディレクトリに設置しても動作します。 -
改善点
WebPが返却されなかった場合もVary: Accept
を返却します。
<IfModule mod_setenvif.c>
# WebP版があるかもしれない画像へのリクエストは全てVaryレスポンスヘッダを返す
SetEnvIf Request_URI "\.(jpe?g|png)$" _image_request
</IfModule>
<IfModule mod_rewrite.c>
# Rewriteモジュールを有効にする
RewriteEngine On
# ブラウザから送信されるAcceptリクエストヘッダがimage/webpを含む場合のみ
# 後続のRewriteRuleを適用する
RewriteCond %{HTTP_ACCEPT} image/webp
# WebP版のファイルがある場合のみ後続のRewriteRuleを適用する
# SCRIPT_FILENAMEを使うことでサブディレクトリの補完が不要に
RewriteCond %{SCRIPT_FILENAME}.webp -f
# *.jpg、*.pngファイルを*.webpファイルに内部的にルーティングする
# Content-Typeはimage/webpにする
# リダイレクト先は $0.webp でも可
RewriteRule .(jpe?g|png)$ %{SCRIPT_FILENAME}.webp [T=image/webp]
</IfModule>
<IfModule mod_headers.c>
# 環境変数_image_requestが真の(上記SetEnvIfが設定された)場合、
# VaryレスポンスヘッダにAcceptを追加する
Header append Vary Accept env=_image_request
</IfModule>
<IfModule mod_mime.c>
# 拡張子.webpファイルはContent-Typeとしてimage/webpを返す
AddType image/webp .webp
</IfModule>