WordPressにスパムコメントや未承認コメントが数千件単位で溜まってしまった場合、管理画面の「コメント」一覧から手作業でチェックを入れて削除を繰り返す方法では膨大な時間がかかります。また、1ページあたりの表示件数を増やして一括削除を実行すると、サーバーの処理時間が上限を超えて「504 Gateway Timeout」エラーで止まってしまう事象が発生します。
本記事では、専用プラグインを追加せずにブラウザの開発者ツール(コンソール)へ貼り付けるだけで、大量のコメントを自動で一気にゴミ箱へ移動するJavaScriptコード(504タイムアウト対策・自動リトライ機能付き)と、スクリプト各行がどのような仕組みで動いているのかを画面キャプチャーとともに解説します。
この記事でわかること
- 大量のコメントを手動・40件一括で消そうとすると時間がかかり、CloudFront等で504エラーが出る原因
- コンソールに貼り付けるだけで使える「10件小分け送信&自動再開版」一括削除スクリプト
- スクリプトのコードの意味(URL整形・DOMParser・CSRFトークン引継ぎ・チャンク分割・自動リトライ)
- 実行中に画面の数字が変わらない理由と、処理を止めずに別タブで進み具合を確認する方法
コメントが溜まると削除に時間がかかる理由と504エラーの壁
WordPress管理画面の「コメント」一覧(edit-comments.php)では、初期状態で1ページあたり20件ずつ表示される仕様になっています。たとえば3,400件のコメントが蓄積されている場合、20件ずつ手動で「すべて選択」→「ゴミ箱へ移動」→「適用」を繰り返すと170回ものページ再読み込みが必要になり、手作業での消去には多大な手間と時間がかかります。
時間短縮のために画面右上の「表示オプション」から1ページあたりの表示件数を40件や100件に変更して一括削除を実行する方法もありますが、WordPress側で1度に大量のコメントをゴミ箱へ移動すると、データベースのステータス更新や件数再集計に時間がかかります。特にサイトの前面にAmazon CloudFrontなどのCDNやリバースプロキシを配置している環境では、オリジンサーバーの応答待ち時間制限(デフォルト30秒)を「40件一括削除の処理時間」が超過した時点で、以下のようなタイムアウトエラーが返されて処理が中断します。
504 Gateway Timeout ERROR
The request could not be satisfied.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Generated by cloudfront (CloudFront) HTTP3 Server
Request ID: SampleRequestId_xxxxxxxxxxxxxxxxxxxx==

溜まってしまった大量のコメントを途中で止まることなく一気に片付けるには、「1回あたりの送信件数を10件ずつに小分けしてタイムアウトを防ぐ仕組み」と、「万が一504エラーが発生しても停止せず自動で続きから再開する仕組み」を組み合わせた自動化スクリプトが有効です。
【コピペで実行】大量コメントを一気に自動削除するスクリプト
以下の手順でWordPress管理画面のコメント一覧を開き、ブラウザのコンソールにスクリプトを貼り付けて実行すると、対象画面のコメントがすべてゴミ箱へ移動されるまでバックグラウンドで自動処理が進みます。画面右上の「表示オプション」の件数(20件や40件など)は変更せず、そのままで実行できます。
実行前の確認ポイント(対象コメントの絞り込み)
このスクリプトは「現在開いているコメント一覧画面に表示されるコメント」を順番にゴミ箱へ移動します。承認済みのコメントを残して未承認コメントだけを一括削除したい場合は、先に管理画面上部の「承認待ち」リンクをクリックして一覧を絞り込んでからスクリプトを実行してください。
実際の操作箇所は以下の画面キャプチャーの通りです。まず削除したいWordPressのコメント一覧画面(edit-comments.php)を開きます(画面内の①)。次にキーボードの F12 キー(Macの場合は Cmd + Option + I、または画面上で右クリックして「検証」)を押して開発者ツールを表示し、「Console(コンソール)」タブを選択します(②)。コンソールの入力欄に下記のスクリプトを貼り付けて Enter キーを押すと自動削除が開始されます(③)。

タイムアウト対策・自動リトライ版スクリプト(完成コード)
(async () => {
const BATCH_SIZE = 10; // 1回に処理する件数(まだ504が出る場合は 5 に変更)
const WAIT_MS = 1500; // 通常時の待機時間(1.5秒)
const RETRY_WAIT_MS = 8000; // 504エラー発生時の待機時間(8秒)
const baseUrl = new URL(location.href);
['ids', 'trashed', 'spammed', 'unspammed', 'approved', 'unapproved', 'deleted'].forEach(k => baseUrl.searchParams.delete(k));
baseUrl.searchParams.set('paged', '1');
const sleep = ms => new Promise(r => setTimeout(r, ms));
let total = 0;
while (true) {
try {
const res = await fetch(baseUrl.toString());
if (!res.ok) throw new Error(`一覧取得エラー (HTTP ${res.status})`);
const html = await res.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
const form = doc.querySelector('#comments-form');
if (!form) throw new Error('フォームの読み込みに失敗');
const allIds = Array.from(form.querySelectorAll('input[name="delete_comments[]"]')).map(cb => cb.value);
if (allIds.length === 0) {
console.log(`🎉 完了しました!(合計 ${total} 件をゴミ箱へ移動)`);
break;
}
const hiddenInputs = Array.from(form.querySelectorAll('input[type="hidden"]')).filter(el => el.name);
const submitUrl = new URL(form.getAttribute('action') || 'edit-comments.php', location.href);
const isPost = form.method.toLowerCase() === 'post';
// 10件ずつ小分けにして送信
for (let i = 0; i < allIds.length; i += BATCH_SIZE) {
const chunk = allIds.slice(i, i + BATCH_SIZE);
const params = new URLSearchParams();
hiddenInputs.forEach(el => params.append(el.name, el.value));
params.set('action', 'trash');
chunk.forEach(id => params.append('delete_comments[]', id));
let delRes;
if (isPost) {
delRes = await fetch(submitUrl.toString(), { method: 'POST', body: params });
} else {
const url = new URL(submitUrl.toString());
url.search = params.toString();
delRes = await fetch(url.toString());
}
if (!delRes.ok) {
throw new Error(`削除処理エラー (HTTP ${delRes.status})`);
}
total += chunk.length;
console.log(`✅ 累計 ${total} 件をゴミ箱へ移動しました...`);
await sleep(WAIT_MS);
}
} catch (err) {
console.warn(`⚠️ ${err.message} → ${RETRY_WAIT_MS / 1000}秒待機して自動再開します...`);
await sleep(RETRY_WAIT_MS);
}
}
location.reload();
})();
※Chromeのコンソールに初めてコードを貼り付ける際、セキュリティ保護機能によって英語または日本語で allow pasting(貼り付けを許可)と入力するようメッセージが表示された場合は、コンソールに allow pasting と手入力して Enter キーを押したあと、もう一度スクリプトを貼り付けて実行してください。
スクリプトのコードの意味と自動削除が動く仕組みを解説
ここからは、上記のスクリプトがブラウザの裏側で具体的にどのような処理を行っているのか、コードのブロックごとに意味を分解して解説します。全体としては「一覧ページのHTML取得」「10件ずつの小分け送信」「エラー時の自動リトライ」の3段階を、対象コメントが0件になるまで自動で繰り返す構造になっています。

先頭の設定定数と非同期ウェイト関数(BATCH_SIZE・WAIT_MS・RETRY_WAIT_MS)
スクリプト全体の外側を囲んでいる (async () => { ... })(); は、即時実行非同期関数(Async IIFE)と呼ばれる書き方です。貼り付けてEnterキーを押した瞬間にその場で関数を実行しつつ、内部で通信完了や待機時間を待つための await 構文を使えるようにしています。
先頭の3つの定数と sleep 関数は、サーバーへの負荷とタイムアウト対策を制御する設定部分です。

| 定数・関数名 | 初期値 | コード内での役割と調整の目安 |
|---|---|---|
| BATCH_SIZE | 10 | 1回のHTTPリクエストでまとめてゴミ箱へ送るコメント件数です。40件表示の一覧であっても10件ずつ4回に分けて送信します。まだ504エラーが出る場合は 5 に減らすと1回あたりの負荷が半分になります。 |
| WAIT_MS | 1500 | 10件送信するごとに挟む通常時の待機時間(ミリ秒=1.5秒)です。連続アクセスによるサーバー高負荷やWAF制限を回避します。 |
| RETRY_WAIT_MS | 8000 | 504タイムアウトなどの通信エラーが発生した際に挟む待機時間(8000ミリ秒=8秒)です。バックエンドのデータベース処理が落ち着くのを待ってから再開します。 |
| sleep(ms) | Promise + setTimeout | 指定ミリ秒だけ処理を一時停止するヘルパー関数です。await sleep(WAIT_MS) と記述することでブラウザをフリーズさせずに待機します。 |
対象URLの整形と「常に1ページ目(paged=1)」を取得する理由
const baseUrl = new URL(location.href);
['ids', 'trashed', 'spammed', 'unspammed', 'approved', 'unapproved', 'deleted'].forEach(k => baseUrl.searchParams.delete(k));
baseUrl.searchParams.set('paged', '1');
ここでは現在ブラウザで開いているURL(location.href)をもとに、一覧取得用のベースURLを作成しています。comment_status=moderated(承認待ちのみ表示)などの絞り込みパラメータはそのまま維持しつつ、直前の手動操作でURLに付与されていた trashed=1 や ids=... といった完了通知用の不要なパラメータだけを searchParams.delete(k) で除去しています。
また、ページ番号を常に paged=1(1ページ目)に固定しているのには明確な理由があります。1ページ目のコメントをゴミ箱へ移動すると、それまで2ページ目以降にあったコメントが自動的に繰り上がって新しい1ページ目になります。そのため、ページ番号を2、3と増やすのではなく、常に最新の1ページ目を取得し続けることで、取りこぼしなく最後の1件まで順番に処理できる仕組みになっています。
HTMLの取得とDOMParserによるコメントID・セキュリティトークンの抽出
const res = await fetch(baseUrl.toString());
if (!res.ok) throw new Error(`一覧取得エラー (HTTP ${res.status})`);
const html = await res.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
const form = doc.querySelector('#comments-form');
const allIds = Array.from(form.querySelectorAll('input[name="delete_comments[]"]')).map(cb => cb.value);
if (allIds.length === 0) {
console.log(`🎉 完了しました!(合計 ${total} 件をゴミ箱へ移動)`);
break;
}
const hiddenInputs = Array.from(form.querySelectorAll('input[type="hidden"]')).filter(el => el.name);
このパートでは、fetch() を使ってブラウザの画面自体は遷移させないまま裏側でコメント一覧ページのHTMLを取得し、DOMParser で解析可能なHTMLドキュメント(doc)に変換しています。
- コメントID一覧(allIds)の取得:WordPressのコメント管理フォーム(
#comments-form)内にある各行のチェックボックスはname="delete_comments[]"という名前を持っており、そのvalue属性に各コメント固有のID番号が入っています。これを配列として取り出し、配列の長さが0(削除対象が1件も残っていない状態)になった時点でループを終了します。 - 隠しフィールド(hiddenInputs)の引き継ぎ:WordPressの管理画面フォームには、外部からの不正リクエスト(CSRF)を防ぐためのワンタイムトークン(
_wpnonceなど)がinput[type="hidden"]として埋め込まれています。これらをすべて抽出して送信データに含めることで、管理画面上で人間がボタンを押したときと同じ正規のリクエストとしてWordPress側に受理されます。
チャンク分割による小分け送信(URLSearchParamsとaction=trash)
for (let i = 0; i < allIds.length; i += BATCH_SIZE) {
const chunk = allIds.slice(i, i + BATCH_SIZE);
const params = new URLSearchParams();
hiddenInputs.forEach(el => params.append(el.name, el.value));
params.set('action', 'trash');
chunk.forEach(id => params.append('delete_comments[]', id));
// ... fetchで送信し、WAIT_MS(1.5秒)待機
}
1ページから取得したコメントID配列(例:40件分の allIds)を、slice(i, i + BATCH_SIZE) によって先頭から10件ずつの小グループ(chunk)に切り出しています。そして URLSearchParams オブジェクトに隠しトークン一式と action=trash(ゴミ箱へ移動する一括操作指定)、および10件分の delete_comments[] をセットして送信します。1回のリクエストが10件分に抑えられるため、WordPress側のデータベース処理が数秒以内で完了し、CloudFrontの30秒制限にかかりにくくなります。
try / catch によるエラー捕捉と自動再開・完了後のリロード
} catch (err) {
console.warn(`⚠️ ${err.message} → ${RETRY_WAIT_MS / 1000}秒待機して自動再開します...`);
await sleep(RETRY_WAIT_MS);
}
もし通信の途中で !delRes.ok(HTTP 504や502など)が返された場合は、throw new Error(...) によって処理が catch (err) ブロックへ移ります。通常のエラーのようにプログラムを異常終了させるのではなく、黄色い警告ログを表示して8秒間(RETRY_WAIT_MS)待機したあと、外側の while (true) ループの先頭に戻って最新の1ページ目を取得し直します。すでにゴミ箱へ移動できた分は一覧から消えているため、重複処理を起こさず残りのコメントから自動で再開されます。そして全件の移動が終わってループを抜けると、最後の location.reload(); で画面が1回だけ再読み込みされ、最新の件数表示に切り替わります。
運用時のポイントと実行中に確認しておきたい挙動
実際に数千件のコメントに対してスクリプトを走らせる際に、知っておくと安心な運用ポイントと進捗の確認方法を整理します。
タイムアウト警告が出ても放置でOK(まだ出る場合はBATCH_SIZEを5へ)
アクセス集中やデータベースのタイミングによってCloudFrontの応答待ち時間制限を超過した場合(画面内の①)、コンソール上に ⚠️ 削除処理エラー (HTTP 504) → 8秒待機して自動再開します... という黄色い警告ログが表示されます(②)。この警告が出てもスクリプトは停止せず、8秒待ってから自動で続きの処理を再開するため、そのまま放置して問題ありません(③)。

もしサーバー負荷が高く、10件ずつの送信でも504エラーが繰り返し表示される環境では、一度ページを再読み込みしてスクリプトを止め、コード2行目の const BATCH_SIZE = 10; を const BATCH_SIZE = 5; に書き換えて再実行すると、1リクエストあたりの処理件数が半分になり安定します。
コンソールは動いているのに画面のコメント件数が変わらない理由
スクリプト実行中、コンソールに ✅ 累計 ○○ 件をゴミ箱へ移動しました... と数字が増えながら表示されていれば、裏側で正常にゴミ箱への移動が進んでいます。
実行中に現在開いている画面上部の すべて (3,411) や ゴミ箱 (708) のような数字が変わらないのは、ブラウザおよびサーバーへの負荷を減らすためにバックグラウンド(fetch 通信)のみで削除リクエストを送り、すべてのコメントの処理が完了した最後に1回だけ画面を再読み込み(location.reload())する設計になっているためです。
実行を止めずに進み具合を確認する方法と「ゴミ箱を空にする」仕上げ
実行中のスクリプトを止めずに実際の減少・増加具合を目視で確認したい場合は、以下の手順で別タブを開いて確認できます。
まず、今スクリプトを実行しているタブは閉じたり更新(リロード)したりせずそのまま維持した状態で、画面上部にある ゴミ箱 リンクを右クリックします(画面内の①)。表示されたメニューから「新しいタブで開く」を選択します(またはWindowsは Ctrl キー、Macは Cmd キーを押しながらクリックでも別タブで開けます)(②)。新しく開いた別タブ側の画面で「ゴミ箱」の件数が増えていれば、問題なく処理が進んでいます(③)。

補足:エラーのみが繰り返される場合と、ゴミ箱移動後の消去手順
- ✅ が一度も出ずエラーだけが続く場合:コンソールに
✅が表示されず、赤いエラーや黄色い⚠️の警告のみが連続する場合は、WordPressのログインセッション切れやレンタルサーバー側のWAF制限が影響している可能性があります。別タブで管理画面に再ログインできるか確認したうえで、BATCH_SIZE = 5やWAIT_MS = 3000(3秒待機)への変更をお試しください。 - ゴミ箱からの本削除(消去):本スクリプトは誤操作時の安全性を確保するため、コメントを一旦「ゴミ箱(
action=trash)」へ移動する仕様になっています。全件の移動が完了したら、「ゴミ箱」一覧画面にある「ゴミ箱を空にする」ボタンをクリックすることでデータベースから消去できます(またはWordPress標準仕様により30日経過後に自動消去されます)。
まとめ
WordPressに大量のコメントが溜まってしまった場合でも、ブラウザのコンソールから小分け送信・自動リトライ対応のスクリプトを実行することで、手作業で何十ページも削除を繰り返したり、504 Gateway Timeoutのたびにやり直したりする手間を省いて自動化できます。
本記事の要点まとめ
- CloudFront等の配下では40件一括削除の処理時間が応答待ち制限(30秒)を超えると504タイムアウトが発生する
- 表示オプションは40件のまま、スクリプト内部で10件ずつ(BATCH_SIZE = 10)に分割送信することでタイムアウトを予防できる
- 万が一504エラーが返っても8秒待機(RETRY_WAIT_MS = 8000)して自動で続きの1ページ目から再開するため放置運用が可能
- 実行中は裏側で通信するため画面の数字は変わらず、進捗を見たい場合は「ゴミ箱」を新しいタブで開いて件数増加を確認する
未承認コメントやスパムコメントが大量に溜まって整理に時間がかかっている際は、ぜひ対象一覧を絞り込んだうえでコンソールからの自動一括移動スクリプトを活用してみてください。
