はじめに:症状は「重い」と「アクセスが増えたように見える」
あるWordPressサイトが、ある時期から急に重くなりました。体感だけでなく、計測でもページ表示に数秒〜十数秒かかることが増え、同時にアクセス解析(あるいは負荷ランキング)の数字が「不自然に増える」ように見えるようになりました。
具体的には、スマホで同じページを**1回更新しただけなのに、ヒット数が+1ではなく+13〜+14**のように増えてしまう現象が起きました。
最初は「ボット?」「マルウェア?」「不正アクセス?」と疑いましたが、結論はまったく違うところにありました。
---
まずやったこと:1回の更新で“何本リクエストが発生しているか”を見える化する
WordPressが重いとき、原因の切り分けでまず重要なのはこれです。
> **「ブラウザが1回のページ表示で、実際に何本HTTPリクエストを追加で出しているか」**
管理画面側に、簡易的な「リクエスト負荷ランキング/トレース」的な仕組み(記録テーブル+表示画面)を用意して、以下の手順で確認しました。
1. 記録ログを全削除
2. スマホで対象ページを**1回だけ**更新
3. 記録されたリクエスト一覧を、**Referer(参照元URL)**で絞り込み
4. `/wp-json/...` や `/admin-ajax.php` が何本出ているかを数える
この時点で、“1回更新で+13〜+14” という体感の正体が見え始めました。
---
判明したこと:ページ表示の裏で REST API が十数回呼ばれていた
ログを見て分かったのは、単にページHTMLを1回取りに行っているだけではなく、ページ表示の直後に **WordPress REST API が連続で呼ばれている**ことでした。
代表的には次のようなものです:
- `GET /wp-json/wp/v2/posts/{ID}?_fields=date` が短時間に大量発生
- 参照元(Referer)がトップページ `/` になっているものが多い
- さらに別のREST(例:PV送信のようなもの)も1本混ざる
ここで重要なのは、
> **アクセスが13倍に増えたのではなく、1回のページ表示の中で“裏リクエスト”が十数本追加で出ていた**
という点です。
アクセス解析や負荷ランキングは「PHPに届いたリクエスト数」をカウントすることが多いので、裏でRESTが大量に走ると、見かけのヒット数も増えたように見えます。
---
次の切り分け:どのページがトリガーになっているか
「裏でRESTが走っている」ことが分かったら、次にやるのは **トリガーの特定**です。
今回のログでは、REST連打の参照元(Referer)が **記事ページではなくトップページ `/`** になっていました。
つまり、
- 「記事ページを開いたときに走っている」のではなく
- **トップページの表示に連動して走っている**
ということが分かります。
この段階で、原因候補はかなり絞れます。
よくあるのは、トップページ内の「記事一覧」「人気記事」「ランキング」「スライダー」などのウィジェットが、表示後に追加処理をしているパターンです。
---
決定打:ページソース検索で “dateだけ取りに行くfetch” を発見
最後はフロント側(HTML/JS)を確認しました。
トップページのページソースを開いて、次の文字列を検索します。
- `_fields=date`
- `wp-json/wp/v2/posts`
- `fetch(`
すると、**記事IDごとに投稿日(date)を取りに行く処理**が見つかりました。
しかも1箇所ではなく、複数のブロック(例:通常一覧、人気記事、スライダー)で似た処理が存在しており、状況によっては同じ記事IDに対して重複して呼ばれる可能性もありました。
この時点で結論は明確です。
> ページが重かった原因は、特定のフロントJSが「記事ごとに REST API を呼ぶ」設計になっていたこと
---
なぜ重くなるのか:1記事=1リクエスト の積み上げ
例えばトップページに13件のカードがあって、各カードの「投稿日を確認するため」にRESTを1回叩く実装だと、ページ表示だけで
- HTML本体:1回
- REST:13回(+場合によっては追加)
- 合計:十数回
になります。
これがキャッシュされず、しかも1本あたりの処理が重いと、体感が一気に悪化します。
さらに「PHPに届くリクエスト数」も増えるため、ログや解析が“水増しされたように”見える現象も起きます。
---
解決:REST連打をやめる(またはバッチ化する)
解決の方針はシンプルです。
1) `<time datetime>` があるなら、それを使って判定(通信ゼロ)
記事一覧に日付情報がHTMLとして出ているなら、JSはそれを読んで “NEW” を判定すれば十分です。
2) `<time>` が無い場合でも「記事ごとにREST」はしない
どうしても日付がHTMLに無いウィジェットがある場合は、
- **必要な記事IDを集めて**
- **1回のバッチ問い合わせでまとめて判定結果を返す**
方式にするのが効果的です。
(例:AJAXでまとめて問い合わせる、あるいは `include=...&_fields=id,date` のようなまとめ取得)_