2026年10月1日
2026年10月1日
WordPressのREST API応答をGZIPで圧縮して転送量を削減する設定
WordPress の REST API が返す JSON は、テキストなので圧縮がよく効き、GZIP をかけると転送量が大きく減ることが多いです。ただし、HTML や CSS は圧縮されているのに、JSON だけ圧縮の対象から漏れているサーバーは珍しくありません。この記事では、REST API の応答が圧縮されているかを確かめる方法、Nginx と Apache での設定、PHP 側で圧縮しないほうがよい理由、圧縮と合わせて応答そのものを小さくする方法を説明します。
まず圧縮されているかを確認する
設定を変える前に、現状を確認します。REST API の応答に Content-Encoding: gzip(または br)が付いていれば、すでに圧縮されています。
# 応答ヘッダーだけを表示する(本文は捨てる)
curl -s -o /dev/null -D - -H "Accept-Encoding: gzip" \
"https://example.com/wp-json/wp/v2/posts?per_page=10"
見るべきヘッダーは次の3つです。
Content-Type:REST API ならapplication/json; charset=UTF-8になっているはずですContent-Encoding:gzipやbrがあれば圧縮済み。何もなければ圧縮されていませんVary:Accept-Encodingが含まれていると、キャッシュが圧縮版と非圧縮版を区別できます
curl -I は HEAD リクエストを送るため、サーバーによっては GET と違うヘッダーを返すことがあります。上のように GET で送り、ヘッダーだけを表示する方法のほうが実際の挙動に近くなります。
効果の大きさは、ダウンロードしたバイト数で比べられます。
# 圧縮なし
curl -s -o /dev/null -w "%{size_download}\n" \
"https://example.com/wp-json/wp/v2/posts?per_page=10"
# 圧縮あり(--compressed で Accept-Encoding を付けて受け取る)
curl -s -o /dev/null -w "%{size_download}\n" --compressed \
"https://example.com/wp-json/wp/v2/posts?per_page=10"
2つの数値がほぼ同じなら、JSON が圧縮されていません。ブラウザの開発者ツールの「ネットワーク」タブでも、転送サイズと実際のサイズの差で確かめられます。
圧縮されない原因:よくある順
1. 圧縮の対象に JSON が含まれていない
もっとも多い原因です。Nginx の gzip_types は、指定しなければ text/html だけが対象です。Apache の mod_deflate も、設定例によっては HTML・CSS・JavaScript だけを列挙していて、application/json が抜けていることがあります。
2. 応答が小さすぎる
Nginx の gzip_min_length などで、一定サイズ未満の応答は圧縮しない設定になっていることがあります。数百バイトの応答は、圧縮してもほとんど小さくならないので、それ自体は問題ではありません。確認するときは per_page=10 程度の、ある程度大きな応答で試します。
3. CDN やリバースプロキシの手前で判断が分かれている
CDN を使っている場合、利用者に届く応答は CDN が圧縮していることがあります。逆に、オリジンのサーバーが圧縮していても、間に入ったプロキシが圧縮を外すこともあります。オリジンに直接リクエストした場合と、CDN 経由の場合とで、ヘッダーを比べると切り分けられます。CDN がどの形式を自動で圧縮するかはサービスごとに異なるため、利用しているサービスのドキュメントで確認してください(2026年10月現在)。
4. 共用サーバーで設定を変えられない
共用サーバーでは、圧縮の設定がサーバー会社の管理になっていることがあります。管理画面に圧縮の設定があるか、.htaccess で mod_deflate の設定が使えるかを、サーバー会社のマニュアルで確認します。
Nginx での設定
http ブロック(または対象の server ブロック)に設定します。設定ファイルを編集する前に、元のファイルを複製して残しておいてください。
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_proxied any;
gzip_types
text/plain
text/css
application/json
application/javascript
application/xml
image/svg+xml;
それぞれの意味は次のとおりです。
gzip_typesにapplication/jsonを入れるのが REST API のための要点です。text/htmlは常に対象なので、ここに書く必要はありませんgzip_comp_levelは 1〜9 で、上げるほど圧縮率は上がりますが CPU を多く使います。中間程度の値で十分なことが多いですgzip_vary onでVary: Accept-Encodingを付けますgzip_proxied anyは、プロキシ経由のリクエストにも圧縮を行う設定です。前段にロードバランサーや CDN がある構成で関係します
変更後は、構文を確かめてから再読み込みします。
sudo nginx -t && sudo systemctl reload nginx
nginx -t でエラーが出たら再読み込みせず、メッセージに出た行を直します。
Apache での設定
Apache では mod_deflate を使います。Apache 2.4 では AddOutputFilterByType を使うのに mod_filter も必要なので、両方が有効になっているかを確認します。
<IfModule mod_deflate.c>
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css
AddOutputFilterByType DEFLATE application/json application/javascript
AddOutputFilterByType DEFLATE application/xml image/svg+xml
</IfModule>
</IfModule>
サーバーの設定ファイルを編集できる場合はそこに書き、共用サーバーなどで編集できない場合は WordPress の .htaccess に書きます。.htaccess に書くときは、WordPress が管理する # BEGIN WordPress から # END WordPress の間には入れず、その外側に置きます。間に入れると、パーマリンク設定の保存時に書き換えられて消えることがあります。
設定ファイルを変更したら sudo apachectl configtest で構文を確認し、問題がなければ Apache を再読み込みします。
PHP 側で圧縮しないほうがよい理由
PHP の zlib.output_compression を有効にしたり、ob_gzhandler を使ったりして、WordPress 側で圧縮する方法もあります。しかし、Web サーバーやプラグインのキャッシュも圧縮を行う構成だと、二重に圧縮されて応答が壊れたり、Content-Encoding が食い違ってブラウザが表示できなくなったりします。圧縮は Web サーバーや CDN に任せ、PHP では行わないのが扱いやすい構成です。
ページキャッシュ系のプラグインに圧縮の設定がある場合も、Web サーバー側の設定と重複していないかを確認します。どこで圧縮しているかを1か所にしておくと、トラブル時に原因を追いやすくなります。
圧縮とあわせて応答を小さくする
圧縮は「同じデータを小さく運ぶ」方法です。そもそも不要なデータを返さないようにすると、圧縮前のサイズも減り、サーバーの処理も軽くなります。
- **
_fieldsで必要な項目だけを返す**:/wp-json/wp/v2/posts?_fields=id,title,linkのように指定すると、指定した項目だけの応答になります - **
per_pageを表示に合わせる**:既定は10件、上限は100件です。表示しない分まで取らないようにします - **
_embedは必要なときだけ**:著者やアイキャッチ画像を埋め込む便利な指定ですが、応答が大きくなります。_fieldsと組み合わせるか、必要な情報だけを別に取ります
curl -s -o /dev/null -w "%{size_download}\n" --compressed \
"https://example.com/wp-json/wp/v2/posts?per_page=10&_fields=id,title,link"
_fields を付けた場合と付けない場合で数値を比べると、圧縮と項目の絞り込みのどちらがどれだけ効いているかが分かります。
セキュリティ面の注意
HTTPS の応答を圧縮すると、条件によっては BREACH と呼ばれる種類の攻撃で、応答に含まれる秘密の値を推測される可能性が指摘されています。条件は、秘密の値(CSRF トークンなど)と、攻撃者が操作できる入力が同じ応答に含まれることです。公開されている投稿一覧のような REST API の応答は該当しにくいものの、ログイン中のユーザー向けに nonce などを含む応答を返す独自エンドポイントを作る場合は、利用者の入力をそのまま応答に含めない設計を心がけてください。
まとめ
curl -D -でContent-Encodingを確認し、--compressedの有無で転送バイト数を比べる- 圧縮されない原因は、JSON が圧縮対象に入っていないことがもっとも多い
- Nginx は
gzip_typesにapplication/json、Apache はAddOutputFilterByType DEFLATE application/jsonを加える。変更前に設定ファイルを複製し、構文チェックをしてから再読み込みする - 圧縮は Web サーバーか CDN の1か所に任せ、PHP では行わない
_fieldsやper_pageで応答そのものを小さくすると、圧縮と合わせて効果が出る