2026年8月16日

2026年8月16日

WordPressで「Invalid nonce」エラーが頻繁に出るときの原因調査と対処

「Invalid nonce」が頻繁に出るとき、原因の多くはnonceそのものの不具合ではなく、「古いnonceが配られている」か「生成時と検証時でユーザーが別人と判定されている」かのどちらかです。この記事では、どのリクエストで失敗しているかを特定する方法と、よくある原因を可能性の高い順に確認・修正する手順を説明します。

まず仕組みを押さえる:nonceが失敗する条件

WordPressのnonceは、次の要素から作られる一時的なトークンです。

  • アクション名(wp_create_nonce( 'save_settings' ) の引数)
  • 現在のユーザーID(ログアウト状態なら0)
  • ログインセッションのトークン
  • 時刻を区切った「tick」と、wp-config.php の NONCE_KEY / NONCE_SALT

標準の有効期間は1日で、wp_verify_nonce() は生成から12時間以内なら1、12〜24時間なら2を返し、それより古いものは false になります。つまり失敗するのは「期限切れ」「ユーザーまたはセッションが違う」「アクション名が違う」「鍵(ソルト)が違う」のいずれかです。この4つのどれに当たるかを切り分けるのが調査の本筋で、メモリ上限やコアファイルの再インストールはほぼ関係ありません。

症状の読み方:どこで失敗しているかを特定する

「Invalid nonce」という文言自体はWordPressコアの標準メッセージではなく、多くはプラグインやテーマが独自に出しているものです。コア側の失敗は、場面によって次のような形で現れます。

  • 管理画面のリンクやフォーム(check_admin_referer()):「リンクの有効期限が切れています」といった確認画面
  • admin-ajax.php(check_ajax_referer()):HTTP 403 と本文 -1
  • REST API(X-WP-Nonce ヘッダー):HTTP 403 とエラーコード rest_cookie_invalid_nonce

ブラウザの開発者ツールを開き、ネットワークタブでエラーが出た瞬間のリクエストを確認してください。見るべき点は、リクエスト先(admin-ajax.php か /wp-json/ か通常のフォーム送信か)、送られているパラメータ(_wpnonce、_ajax_nonce、security などの名前が多い)、そしてレスポンスのステータスと本文です。nonceのパラメータ自体が空なら、生成側のJavaScriptが読み込まれていないなど別の問題です。

失敗をログに記録する

再現が不定期なら、失敗したときだけ記録するmu-pluginを一時的に置くと原因が見えやすくなります。wp_verify_nonce_failed はnonceの検証に失敗したときにコアが呼ぶアクションです(nonceが空の場合は呼ばれません)。作業前にバックアップを取り、できればステージング環境で試してください。

<?php
/**
 * Plugin Name: Nonce Failure Logger (一時調査用)
 * 置き場所: wp-content/mu-plugins/nonce-failure-logger.php
 * 調査が終わったら削除してください。
 */
add_action( 'wp_verify_nonce_failed', function ( $nonce, $action, $user, $token ) {
	if ( ! defined( 'WP_DEBUG_LOG' ) || ! WP_DEBUG_LOG ) {
		return;
	}
	$path = '';
	if ( isset( $_SERVER['REQUEST_URI'] ) ) {
		// クエリ文字列(nonce値を含むことがある)は記録しない
		$path = (string) wp_parse_url( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), PHP_URL_PATH );
	}
	error_log( sprintf(
		'[nonce-failed] action=%s user_id=%d session=%s path=%s',
		is_scalar( $action ) ? (string) $action : 'non-scalar',
		( $user instanceof WP_User ) ? $user->ID : 0,
		$token ? 'yes' : 'no',
		$path
	) );
}, 10, 4 );

wp-config.php で WP_DEBUG と WP_DEBUG_LOG を true、WP_DEBUG_DISPLAY を false にしておくと、wp-content/debug.log に記録されます。nonceの値そのものはログに残さない設計にしています。ログで user_id=0 が出ていればログイン状態が伝わっていない、特定のアクション名だけが繰り返し出ていれば、そのアクションを使うプラグインが原因の候補になります。

原因と対処(可能性の高い順)

1. ページキャッシュが古いnonceを配っている

最も多い原因です。フォームを含むページがページキャッシュやCDNに24時間以上保存されると、そこに埋め込まれたnonceは期限切れになり、訪問者が送信するたびに失敗します。「朝は動くのに夕方になると失敗する」「キャッシュを削除すると直る」なら、ほぼこれです。

確認方法は、失敗するページのHTMLソースを開き、キャッシュプラグインやCDNが付けるコメントやレスポンスヘッダー(age や x-cache など)で、いつ生成されたものかを見ることです。

対処は次のいずれかです。

  • フォームのあるページ(問い合わせ、カート、会員ページなど)をキャッシュ対象から外す
  • キャッシュの有効期間を12時間より短くする(ログアウト中の訪問者向けのnonceは全員共通なので、期間内なら問題になりません)
  • nonceをページ読み込み後にAjaxで取得する方式に変える(プラグイン側が対応している場合)

2. ログイン状態が検証時に伝わっていない

ログイン中に生成したnonceを、ログインCookieが届かないリクエストで検証すると、ユーザーIDが0として扱われ失敗します。前述のログで user_id=0 が出るのが目印です。

よくある例は、www あり・なしの混在、http と https の混在、「WordPressアドレス」と「サイトアドレス」の不一致、リバースプロキシやCDNがCookieを落としている構成です。設定画面のURLとブラウザのアドレスバーのURLが一致しているかを確認し、リダイレクトでどちらかに統一してください。REST APIを自作のJavaScriptから呼んでいる場合は、wp_create_nonce( 'wp_rest' ) で作った値を X-WP-Nonce ヘッダーに付けているか、fetch の credentials 設定でCookieを送っているかも見直します。

3. 長時間開いたままの編集画面とHeartbeatの停止

管理画面では、Heartbeat APIが定期的に通信してnonceを更新しています。サーバー負荷対策でHeartbeatを止めたり間隔を極端に延ばしたりしていると、何時間も開いていた編集画面で保存したときに失敗します。最適化系プラグインの設定を確認し、少なくとも投稿編集画面ではHeartbeatを有効に戻してください。ログアウトして再ログインした場合もセッションが変わるため、別タブで開いていた古い画面のnonceは使えなくなります。

4. 認証用キー(ソルト)の変更やサーバー間の不一致

NONCE_KEY や NONCE_SALT を変更すると、それ以前に発行されたnonceはすべて無効になります。セキュリティプラグインが定期的にソルトを再生成する機能を持っていることもあるので、エラーが出始めた時期と設定変更の履歴を照らし合わせてください。複数台のWebサーバーで負荷分散している場合は、全台のwp-config.phpのキーが同じか、サーバーの時刻がNTPで同期されているかも確認が必要です。

5. プラグインやテーマの実装ミス

生成時と検証時でアクション名が一致していない、ログイン前に作ったnonceをログイン後に検証している、といった実装の問題もあります。ログで特定のアクション名だけが失敗している場合は、該当プラグインを停止して再現するかを確認し、最新版への更新や開発元への報告を検討します。自作コードなら wp_create_nonce() と check_ajax_referer() に同じ文字列を渡しているかを見直してください。

有効期間を延ばすのは最後の手段

nonce_life フィルターで有効期間を延ばすことはできますが、問題を先送りにするだけで、攻撃に使える時間も延びます。キャッシュやCookieの問題を直さずに延長しても、時間がたてば同じエラーが出ます。どうしても必要な場合だけ、子テーマのfunctions.phpかmu-pluginに次のように書きます。

// nonceの有効期間を2日に延ばす(標準は1日)。原因を解消できない場合の暫定措置。
add_filter( 'nonce_life', function () {
	return 2 * DAY_IN_SECONDS;
} );

WordPressのバージョンによっては、このフィルターにアクション名が第2引数として渡されるため、特定の処理だけ延ばすこともできます(2026年10月現在)。使う前に利用中のバージョンのコードリファレンスで確認してください。

まとめ

  • nonceの失敗は「期限切れ」「ユーザー・セッション違い」「アクション名違い」「鍵違い」のどれかに当たる
  • 開発者ツールで失敗したリクエストを特定し、必要なら wp_verify_nonce_failed でユーザーIDとアクション名を記録する
  • 最も多いのはページキャッシュによる古いnonce。次にURLの不一致によるCookie未送信、Heartbeatの停止、ソルト変更の順に確認する
  • 有効期間の延長は暫定措置にとどめ、調査用のmu-pluginは終わったら削除する

お気軽にご相談ください

お見積りへ お問い合わせへ
▲