Category Archive お悩み・トラブル解決

WordPress 7.1で標準サイトマップが404になる原因と解消手順

WordPress 7.1で標準サイトマップが404になる原因と解消手順

WordPress 7.1 に更新した後、プラグインなしのサイトで標準 XML サイトマップが 404 になる場合は、パーマリンク設定の再保存でリライトルールを再生成する。同じ環境で 7.0.4 では正常だった場合は WordPress 7.1 本体のリグレッションの可能性が高いため、急ぎなら 7.0.4 へ戻すのが確実だ。

WordPress 7.1 で標準サイトマップが 404 になる原因

WordPress 7.1 で標準サイトマップが 404 になる原因

標準 XML サイトマップは WordPress 5.5 から搭載された機能だ。パーマリンク設定に基づき wp-sitemap.xml という URL でインデックスを出力し、wp-sitemap-posts-page-1.xml のように投稿タイプ別・ページ番号別の子サイトマップを生成する。検索エンジンがサイトを巡回する入り口の役割を持つ。

WordPress 7.0.4 から 7.1 へ更新した直後から wp-sitemap.xmlwp-sitemap-posts-page-1.xml の両方が 404 を返す場合は、プラグインの競合やテーマの影響を疑う前に、WordPress 本体のリライト処理と更新後のルール再生成状況を確認する。特に Nginx を Apache の前段に置くリバースプロキシ環境では、拡張子 .xml を静的ファイルとして振り分ける設定や、Apache 側の .htaccess にリクエストが届かない構成が原因になることがある。

同じ環境・同じテーマ・プラグインなしで 7.0.4 に戻すと直る場合、WordPress 7.1 の標準サイトマップ機能に起因する不具合の可能性が高い。設定を見直しても直らないときは、コア側の更新によるリグレッションを視野に入れる。

最初に試すパーマリンク再保存とキャッシュ確認

最初に試すパーマリンク再保存とキャッシュ確認

WordPress はバージョン更新後に、パーマリンクのリライトルールが内部にキャッシュされたままになることがある。最初に管理画面の「設定」→「パーマリンク」を開き、内容を変更せずに「変更を保存」を押す。これによって .htaccess やデータベース上のルールが再生成される。

保存後は、ブラウザのシークレットウィンドウか curlwp-sitemap.xml の HTTP ステータスコードを確認する。キャッシュ系プラグインを入れていなくても、レンタルサーバーや Nginx、CDN がレスポンスをキャッシュしている可能性があるため、まずキャッシュを削除する。

STEP 1 サイトとデータベースのバックアップを取る
STEP 2 「設定」→「パーマリンク」を再保存してリライトルールを再生成する
STEP 3 Nginx で静的ファイルとして処理していないか、Apache の .htaccess を確認する
STEP 4 それでも直らなければ WordPress 7.0.4 へ一時的に戻す

この切り分けで、リライトルールの再生成だけで直るのか、Nginx と Apache の設定まで必要なのかが区別できる。

ここで 200 OK に戻れば、更新によるリライトルールの再生成漏れが原因だったことになる。それでも 404 なら次の転送設定の確認へ進む。

Nginx リバースプロキシと Apache の転送設定を確認する手順

Nginx リバースプロキシと Apache の転送設定を確認する手順

Nginx をリバースプロキシとして Apache の前に置く構成では、location /proxy_pass で Apache へ向いているかを確認する。もし location ~* \.(xml)$ のような拡張子判定があり、Nginx が XML を静的ファイルとして処理してしまうと、WordPress に到達せず 404 になる。

Apache 側の .htaccess は、# BEGIN WordPress から # END WordPress の間に標準の mod_rewrite.c ブロックがあるかを確認する。独自の RewriteCond や RewriteRule を追記していないこと、RewriteBase / がサイトの設置パスに合っていることが重要だ。

curlexample.com/wp-sitemap.xmlexample.com/?sitemap=posts&sitemap-subtype=page&paged=1 をそれぞれ確認する。クエリ形式でも 404 なら、WordPress がサイトマップを出力できていないか、Nginx から Apache への転送が正しくない可能性がある。

WordPress 7.1 から 7.0.4 へ戻す一時対処と注意点

WordPress 7.1 から 7.0.4 へ戻す一時対処と注意点

設定変更でも症状が変わらない場合は、バックアップを取得したうえで WordPress 7.0.4 へ戻す。公式パッケージの 7.0.4 でコアファイルを置き換え、管理画面にデータベース更新の案内が出た場合はそれを実行する。

Before 404
example.com/wp-sitemap.xml を開く
HTTP ステータス 404 Not Found
Google Search Console で「取得できませんでした」と表示される
After 200
同じ URL を開く
HTTP ステータス 200 OK
XML サイトマップが正常に出力される

このデモは 7.1 で 404 を返していたサイトマップが、7.0.4 へ戻すと 200 OK に変わることを示している。

ファイルとデータベースのバックアップを取らずにダウングレードすると、予期しない不整合からの復旧が難しくなる。WP Downgrade のようなダウングレード用プラグインを使う場合も、更新前のスナップショットが必須になる。

7.0.4 へ戻すと、同じ URL wp-sitemap.xml が正常に XML を返すようになる。ただし 7.1 の修正版がリリースされたら、そのまま使い続けずに安全なタイミングで更新する。

Google Search Console の再読み込みと標準サイトマップの再送信

Google Search Console の再読み込みと標準サイトマップの再送信

サイトマップが正常に戻ったら、Google Search Console の「サイトマップ」で登録済みの wp-sitemap.xml を確認する。「取得できませんでした」と表示されていた場合は再読み込みを行い、ステータスが「成功しました」に変わるのを待つ。

古いレポートが残っている場合は、一度サイトマップを削除してから wp-sitemap.xml を再送信する。フェッチが完了するまで数分かかることがあるため、すぐに結果が出なくても時間を置いて再確認する。

再発防止と WordPress 7.1 の修正状況の追い方

再発防止と WordPress 7.1 の修正状況の追い方

WordPress 7.1 の後続リリースで標準サイトマップの修正が含まれるかは、ダッシュボードの更新通知と WordPress のリリース情報で確認する。本番環境へメジャー更新を適用する前には、ステージング環境で wp-sitemap.xml が 200 OK を返すことを必須のチェック項目にする。

Nginx と Apache を併用している構成では、更新前後の curl の応答コードを記録しておくと、今回のような更新起因のリグレッションを素早く特定できる。SEO プラグインのサイトマップで代替することも一時的には可能だが、パーマリンク全体の不具合を隠す可能性があるため、先に WordPress 本体とサーバー設定の切り分けを行う。

よくある質問

標準サイトマップが404になったらまず何をすればいい?

管理画面の「設定」→「パーマリンク」を開き、内容を変えずに保存してリライトルールを再生成する。これで直らない場合は、Nginx の静的ファイル判定や Apache の .htaccess を確認し、それでも再現するなら WordPress 7.0.4 へ戻して切り分ける。

Nginx リバースプロキシだと何が問題になる?

Nginx が .xml のリクエストを静的ファイルと判断すると、Apache へ渡さずに 404 を返す。Apache の前段で location 設定を見直し、WordPress の index.php までリクエストが届く構成になっているか確認する。

WordPress 7.1 に更新しない方がいい?

通常の単純な LAMP 構成では問題が起きにくいが、標準サイトマップを運用中のサイトで更新直後の 404 が許容できないなら、修正版が出るまで 7.0.4 を使う判断も現実的だ。本番更新前にステージングで確認するのが基本になる。

7.0.4 に戻した後、Google Search Console で何をすればいい?

サイトマップの再読み込みを行い、ステータスが成功に変わるのを確認する。エラーが残っている場合は一度削除して wp-sitemap.xml を再送信し、数分後にもう一度確認する。

SEO プラグインのサイトマップに切り替えてもいい?

切り替え自体は可能だが、WordPress 標準のサイトマップが壊れた原因を残したままだと、他のパーマリンクでも同様の不具合が出る可能性がある。一時的な代替には使えるが、本体側の切り分けを先に行う。

この記事のポイント

  • WordPress 7.1 更新後に標準サイトマップが 404 になる場合、最初にパーマリンクを再保存する
  • Nginx と Apache の組み合わせでは静的ファイル判定や .htaccess を確認する
  • 7.0.4 で同じ環境が正常なら WordPress 本体のリグレッションを疑う
  • 急ぐ場合はバックアップを取って 7.0.4 へ戻すのが確実
  • 修正版のリリースと Google Search Console のステータスを確認する
WP-Stagingサイトが重大なエラーで表示されない原因と直し方

WP-Stagingサイトが重大なエラーで表示されない原因と直し方

WP-Stagingの複製サイトにアクセスすると「このサイトで重大なエラーが発生しました」と表示され、デバッグログに call_user_func_array の TypeError が記録される場合、PHP 8系ではコアファイルの欠落や混在が致命的エラーへ変わる。PHP 7.4系へ一時的に切り替えて管理画面へ入り、WordPressコアの再インストールとプラグイン更新を行えば根本から直る。

WP-Stagingサイトが重大なエラーになる原因

WP-Stagingサイトが重大なエラーになる原因

デバッグログの最終行には call_user_func_array() が「Argument #1 ($callback) must be a valid callback」という趣旨の TypeError を投げ、_wp_register_default_icon_collections という関数が見つからないことが記録される。この関数は WordPress 本体の wp-includes/theme.php で定義されるもので、本来は必ず存在しているはずだ。

WP-Staging はライブサイトのファイルとデータベースをコピーしてステージングサイトを作る。このとき wp-includes 内のファイルが不完全にコピーされていたり、旧バージョンのコアファイルと新バージョンが混在したりすると、関数が未定義のまま WordPress の読み込み処理が進み、このエラーが発生する。

PHP 8.0 以降は call_user_func_array() に存在しない関数を渡すと、警告ではなく TypeError を投げて処理を停止する。PHP 7.4 までは警告を出して読み込みが続いたため、バージョンを下げると画面が復帰する。ただし原因であるコアファイルの欠落は残ったままなので、根本解決にはならない。

同じログに Wordfence や ThemeWhizzie の Deprecated 警告も出ている。これらは PHP 8 で廃止予定になった古い記法への警告で、単体ではサイトを落とさない。ただ PHP 8 非対応の古いコードが残っている証拠なので、更新で片付けておく必要がある。

debug.logに記録されたエラーを読み分ける

debug.logに記録されたエラーを読み分ける

WordPress のデバッグ情報は wp-content/debug.log に保存される。今回のログには Deprecated 警告が数件並び、最後に Fatal error が1件出ている。サイトが落ちる直接原因は Fatal error の1件だけだ。まだログを有効にしていない場合は、wp-config.php を編集して次の3行を追加する。

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

Fatal error の内容は「呼び出そうとした関数が見つからない」という意味だ。_wp_register_default_icon_collections が WordPress 本体に無いことから、コアの関数定義が不足していると判断できる。Wordfence の Non-canonical cast や ThemeWhizzie の Creation of dynamic property は、PHP 8 で非推奨となった書き方を警告しているにすぎない。

PHP 7.4系へ切り替えて管理画面に戻る

PHP 7.4系へ切り替えて管理画面に戻る

最初の応急処置として、サーバーの PHP バージョンを 7.4 系へ変更する。ホスティングの管理画面にある「PHP バージョン管理」や「PHP Selector」などから選択するのが一般的だ。IIS 10 で FastCGI を使っている場合は、php-cgi.exe のパスを 7.4 系へ変更する。切り替え後はステージングサイトを再読み込みして、管理画面に入れるか確認する。

PHP 8系での動作
call_user_func_array() が TypeError を投げる
致命的エラーとして処理が停止する
画面に「重大なエラー」と表示される
PHP 7.4系での動作
同じ呼び出しが警告(Warning)になる
処理がスキップされて読み込みが継続する
管理画面へ復帰できる

PHP 8 系ではコールバック不足が TypeError で致命化し、PHP 7.4 系では警告として読み込みが継続する。この違いにより、バージョンを下げると一時的にサイトが復帰する。

ただし PHP 7.4 系はセキュリティサポートが終了している。常用するのは避け、次の手順で WordPress 本体とプラグインを整えた後、PHP 8 系へ戻すのが安全だ。

WordPressコアを再インストールして欠落ファイルを戻す

WordPressコアを再インストールして欠落ファイルを戻す

根本原因は WordPress 本体のファイルが不完全なことだ。管理画面に戻れたら、ダッシュボードの「更新」画面から現在のバージョンを再インストールするのが最も簡単だ。ファイルの欠落や破損が解消され、関数が再び見つかるようになる。

STEP 1 PHP を 7.4 系へ切り替える
STEP 2 管理画面からコアを再インストールする
STEP 3 Wordfence とテーマを更新する
STEP 4 PHP 8 系へ戻して再発を確認する

復旧手順の全体像は上のとおり。各ステップの詳細を以下に示す。

ダッシュボードからコアを再インストールする

管理画面の「ダッシュボード」→「更新」を開くと、「WordPress の最新バージョンを実行しています」の下に「バージョン XX を再インストール」というボタンが表示される。ここを押せば、同じバージョンのコアファイルが公式配布パッケージから取得され、欠落している wp-includes 内のファイルが上書きされる。

再インストール後にステージングサイトを更新し、重大なエラーが消えたかを確認する。もし管理画面にも入れない状態なら、次の FTP で対処する方法を試す。

管理画面に入れないなら FTP で wp-includes を上書きする

ステージングサイトにアクセスできない場合、FTP または SFTP でサーバーへ接続する。WordPress の公式サイトから同じバージョンのパッケージを取得し、その中の wp-includeswp-admin フォルダを、壊れているステージング側へ上書きアップロードする。

wp-content フォルダは上書きしない。ここにはテーマやプラグイン、アップロード画像が入っており、上書きすると設定が消える危険がある。あくまでコアのファイルだけを入れ替えるのが安全だ。

ステージングサイトを作り直す判断

破損の範囲が広い場合や、エラーが複数回繰り返される場合は、WP-Staging でステージングサイトを作り直すのも有効だ。その際、除外設定やファイルコピーの途中で止まっていないか、保存容量が十分かを確認する。ライブサイト側が正常なら、再作成のほうが速く済むことも多い。

Wordfenceと使用テーマをPHP 8対応版へ更新する

Wordfenceと使用テーマをPHP 8対応版へ更新する

Fatal error とは別に、Wordfence と ThemeWhizzie から Deprecated 警告が出ている。これは今すぐサイトを落とすものではないが、PHP 8 系で廃止された書き方を使っており、将来の PHP 更新で同じく致命的エラーへ変わる可能性がある。根本解決の段階でまとめて更新しておく。

Wordfence はプラグインの更新画面から最新版へ更新できる。テーマが市販テーマで更新が止まっている場合は、PHP 8 非対応の警告が出続けるため、テーマの差し替えや該当するウィザード機能の無効化を検討する。更新が終わったら PHP を 8 系へ戻し、ステージングサイトが表示されるか、デバッグログに Fatal error が出ないかを確認する。

よくある質問

PHP 7.4に戻すだけで問題は解決するのか

サイトは一時的に復帰するが、コアファイルの欠落や混在という原因は残る。PHP 7.4 系はセキュリティサポートが終了しているため、WordPress コアの再インストールで根本解決してから PHP 8 系へ戻すのが正しい流れだ。

管理画面にも入れないときはどうするのか

FTP または SFTP でサーバーへ接続し、WordPress 本体の wp-includeswp-admin を公式パッケージから上書きする。その前に wp-config.php でデバッグログを有効にしておくと、上書き後もエラーが出る場合に原因を特定しやすい。

WordPressコアの再インストールでデータは消えるのか

ダッシュボードからの再インストールでは wp-content を触らないため、テーマやプラグイン、アップロード画像、データベースの内容は消えない。念のため実行前にバックアップを取るのが安全だ。

ステージングサイトを作り直した方が速いのか

ファイル破損の範囲が広い場合や再発を繰り返す場合は、WP-Staging で作り直す方が確実だ。その際は除外設定と保存容量を確認し、ライブサイト側が正常であることを先に確かめておく。

Wordfenceの警告をPHP 8のままで消せるのか

Wordfence を最新版へ更新すれば大半の Deprecated 警告は消える。テーマ側の警告が残る場合は、該当するウィザード機能を無効化するか、PHP 8.1 系を選んで一時的に警告を抑える方法もある。ただし警告を無理に隠すより、コードを更新する方が安全だ。

この記事のポイント

  • call_user_func_array の TypeError はコアファイル欠落が PHP 8 で致命化したもの
  • PHP 7.4 への切り替えは応急処置で根本解決ではない
  • 管理画面から WordPress コアを再インストールして欠落ファイルを戻す
  • Wordfence とテーマを更新し PHP 8 へ戻して確認する
  • ステージング作成時の除外設定やファイルの完全性も確認する
WordPressでデータベースのテーブルが見つからないエラーの原因と対処法

WordPressでデータベースのテーブルが見つからないエラーの原因と対処法

WordPressで「テーブルが見つからない」エラーが表示されたら、まずwp-config.phpのテーブル接頭辞($table_prefix)とデータベース内の実際のテーブル名を照合する。接頭辞が一致していなければ設定を修正し、そもそもテーブルが存在しない場合はバックアップからの復元が必要になる。

なぜWordPressで「テーブルが存在しない」エラーが発生するのか

なぜWordPressで「テーブルが存在しない」エラーが発生するのか

WordPressは投稿や固定ページ、ユーザー情報をすべてデータベースに保存している。データベースの中には複数のテーブルがあり、それぞれ「接頭辞+テーブル名」という形式で管理される。接頭辞はセキュリティ対策としてサイトごとに変えられる仕組みだ。たとえば初期状態ではwp_posts、wp_optionsという名前になる。

この接頭辞はwp-config.phpという設定ファイルの$table_prefixで指定する。実際のデータベースにあるテーブル名と、この設定ファイルの値が食い違うと、WordPressが存在しない名前のテーブルを探しに行き「テーブルが見つからない」エラーを起こす。サイト移転や手動バックアップの復元時に、データベースだけ別の接頭辞で持ってきてしまった場合に起きやすい。

データベース修復機能は破損したテーブルを直すためのもので、存在しないテーブルを新しく作ることはできない。そのため「修復」を実行してもエラーは解消されない。問題の切り分けには、まず実際にどんなテーブルが存在するのかを確認する必要がある。

データベース内の実際のテーブル接頭辞を調べる方法

データベース内の実際のテーブル接頭辞を調べる方法

エラーの原因が接頭辞の不一致か、それともテーブル自体が消えているのかを切り分けるには、データベース管理ツールを開いて実在するテーブル名を確認する。レンタルサーバーの管理画面にログインし、phpMyAdminと呼ばれるデータベース管理ツールを選択するのが最も確実だ。

STEP 1 レンタルサーバーの管理画面にログインする
STEP 2 phpMyAdmin またはデータベース管理を開く
STEP 3 WordPress が使うデータベースを選択する
STEP 4 左側のテーブル一覧に並ぶ名前の先頭部分を確認する

テーブル一覧にwp_postsやwp_optionsという名前が見えたら、実際の接頭辞はwp_だ。この場合、設定ファイルが別の値を指していることが原因なので、次の手順で修正する。一方、テーブル一覧が空だったり、別の名前でも見当たらない場合は、テーブル自体が失われている可能性が高い。

wp-config.phpのテーブル接頭辞を正しい値に直す手順

wp-config.phpのテーブル接頭辞を正しい値に直す手順

設定ファイルの修正は、FTPソフトかレンタルサーバーのファイルマネージャでwp-config.phpを直接編集する。WordPressをインストールしたルートディレクトリにあるこのファイルを開き、$table_prefixが書かれた行を探す。

変更前 $table_prefix = ‘jos_’;
修正後 $table_prefix = ‘wp_’;

保存後にサイトを再読み込みすると、これまで表示されていたテーブル関連のエラーが消えて通常の画面が戻る。管理画面にもアクセスできるようになるはずだ。なお、wp-config.phpを編集する前には必ずファイルのバックアップを取っておくこと。

ファイルマネージャで編集できない場合は、FTPソフトでサーバーに接続して同じファイルをダウンロードし、テキストエディタで修正してアップロードする。文字コードはUTF-8のまま保存する。

テーブルが実際に消えている場合の復元方法

テーブルが実際に消えている場合の復元方法

phpMyAdminで確認しても該当するテーブル自体が存在しない時は、設定の不一致ではなくデータが失われている状態だ。この場合は修復機能では戻らないため、バックアップからの復元が必要になる。

  1. レンタルサーバーの自動バックアップ機能を確認する
  2. 運用中に取得していたバックアップデータから復元する
  3. ホスティング事業者のサポートに問い合わせる

バックアップが手元にない場合でも、ホスティング事業者が定期的にサーバー全体のバックアップを保持していることが多い。データベースだけを復元できるか、サポートに確認するのが得策だ。復元後は接頭辞の一致を必ず確かめる。

テーブル接頭辞の不一致を防ぐための確認ポイント

テーブル接頭辞の不一致を防ぐための確認ポイント

サイト移転やバックアップ復元の後に同じエラーを再発させないためには、接頭辞とデータベースの状態を習慣的に確認しておくとよい。最低限のポイントを挙げる。

  • サイト移転時は設定ファイルとデータベースの接頭辞を照合する
  • テスト環境と本番環境では同じ接頭辞にそろえる
  • wp-config.php を編集する前に必ずバックアップを取る
  • 不要なデータベース修復を連続実行しない

よくある質問

接頭辞を直したのにまだテーブルが見つからないエラーが出る

設定ファイルを保存した後もブラウザやサーバーのキャッシュが残っていると表示が変わらない場合がある。ブラウザをスーパーリロードし、サーバー側のキャッシュを削除してから確認する。それでも出る場合は、実際に存在するテーブル名と設定値をもう一度照合し、データベース名やホスト名も確認する。

phpMyAdmin を使わずに接頭辞を確認できるか

wp-config.php の設定だけでは実際のテーブル名は分からない。データベース管理ツールか、ホスティング事業者の管理画面にあるデータベース一覧で確認する。どうしても見つからない時はサポートに問い合わせると接頭辞を教えてもらえる場合がある。

WordPress の修復機能でテーブルが消えることはあるか

修復機能は既存テーブルの破損を修復するのが目的で、テーブル自体を削除する動作ではない。ただし存在しないテーブルは作成できないため、修復を実行してもテーブルがないエラーは解消しない。元のテーブルが消える主な原因はデータベースの操作ミスやインポート時の上書きだ。

バックアップからデータベースだけ復元する手順は

ホスティングの管理画面にあるバックアップ機能を開き、復元したい日時のデータベースを選ぶ。phpMyAdmin からインポートできる SQL ファイルがある場合は、該当するデータベースを選択してインポートを実行する。操作前に現在のデータを追加でエクスポートしておくと安全だ。

接頭辞を wp_ に変更すると既存データは消えるか

接頭辞の変更そのものはテーブル名を読み替えるだけで、データを削除しない。ただし実在しない接頭辞に変更すると WordPress がテーブルを見つけられず、同じエラーになる。必ずデータベース内の実名と一致する値に変更する。

この記事のポイント

  • テーブルが見つからないエラーは接頭辞の不一致が第一原因
  • phpMyAdmin で実際のテーブル名を確認する
  • wp-config.php の $table_prefix を実在の接頭辞に合わせる
  • テーブル自体が無い時はバックアップ復元が最短ルート
  • 設定変更前に必ず wp-config.php とデータベースのバックアップを取る
WooCommerceのApple PayとGoogle Payがクラシックカートで表示されない時の直し方

WooCommerceのApple PayとGoogle Payがクラシックカートで表示されない時の直し方

ECサイトでWooCommerceのクラシックカートとクラシックチェックアウトにApple PayやGoogle Payのボタンが表示されない場合、原因の多くはStripeが提供するホスト型決済方法設定がショートコード版ページのJavaScript初期化と衝突していることにある。商品ページやブロックカートでは正常でも、ショートコード版だけ動かないという状況では、決済ゲートウェイの初期化スクリプトが正しく読み込まれていない可能性が高い。

なぜクラシックカートだけApple PayとGoogle Payが表示されないのか

なぜクラシックカートだけApple PayとGoogle Payが表示されないのか

WooCommerceのStripeゲートウェイは、ページの種類に応じて決済ボタンを表示するためのJavaScriptを別々のタイミングで初期化する。ブロックカートとブロックチェックアウトは新しいGutenbergブロックとしてレンダリングされるため、StripeのExpress Checkoutボタンを配置する専用のコンテナが自動で用意される。

一方、クラシックカートとクラシックチェックアウトはショートコード([woocommerce_cart][woocommerce_checkout])でページに埋め込まれる。この方式では、テーマのJavaScriptやjQueryの読み込み順によってStripeの初期化スクリプトが正しく実行されず、ボタンが表示されないことがある。特にカスタムテーマやテーマビルダーを使っている場合は競合が起きやすい。

もう一つの有力な原因が、WooCommerceのログに出力されている「Stripe-hosted payment method configuration(ホスト型決済方法設定)」への切り替えエラーだ。Stripeがホストする決済方法設定に切り替わると、従来のローカル設定で動いていたExpress Checkoutボタンの初期化ロジックと整合しなくなることがある。これはStripeアカウント側の設定変更によって発生する。

つまり、症状がショートコード版ページに限定されるのは、決済ゲートウェイ自体の設定ミスではなく、ページのレンダリング方式とStripeスクリプトの初期化タイミングのずれが主因であるケースがほとんどだ。

JavaScriptコンソールでエラーを確認する手順

JavaScriptコンソールでエラーを確認する手順

真っ先に調べるべきは、クラシックカートとクラシックチェックアウトのページでJavaScriptエラーが出ているかどうかだ。ブラウザのデベロッパーツールを使えば、原因となるスクリプトを特定できる。

Chromeの場合、クラシックカートのページを開いた状態で「F12」キーを押し、「Console」タブを確認する。赤いエラーメッセージが表示されていれば、その内容を記録する。特にwc_stripe_upe_paramswp.escapeHtmlに関連するエラーはStripeゲートウェイの初期化失敗を示す代表的なものだ。

FirefoxやEdgeでも同様に、開発者ツールのコンソールから確認できる。スマートフォンで確認する場合は、パソコンのブラウザでデベロッパーツールを開き、デバイスエミュレーションモードにして同じページを読み込むとよい。

Stripeのホスト型決済方法設定を無効化する

Stripeのホスト型決済方法設定を無効化する

WooCommerceのログにホスト型決済方法設定への切り替えエラーが出ている場合、Stripeダッシュボード側の設定を変更するか、プラグイン側でホスト型設定を手動設定に戻す必要がある。

STEP 1 Stripeダッシュボードにログインし「Payment Method Configurations」を開く
STEP 2 作成済みの設定がホスト型になっていないか確認する
STEP 3 WooCommerce管理画面の「Stripe設定」で決済方法を手動設定に切り替える
STEP 4 クラシックカートとチェックアウトでボタンが表示されるか再テストする

このデモはStripe設定の確認から修正までの手順の流れを示したものだ。実際の管理画面の項目名はWooCommerce Stripeプラグインのバージョンによって若干異なる。

Stripeダッシュボードでは、ホスト型決済方法設定が有効になっていると、決済方法の組み合わせがStripeサーバー側で管理される。これがWooCommerce側のExpress Checkoutボタン初期化と競合し、クラシック版ページでのみボタンが出ない状態を引き起こすことがある。WooCommerce側のStripe設定画面で「決済方法を手動で管理」するオプションを選択し、Apple PayとGoogle Payを明示的に有効化すると改善するケースが多い。

プラグインの再設定だけでは直らない場合は、Stripeアカウントの接続を一度解除して再接続するのも有効だ。WooCommerceの「決済」タブからStripeを選び、「接続を解除」を押した後、再度Stripeアカウントで接続する。この操作でプラグインがStripeサーバーから最新の設定を取得し直し、ホスト型決済方法設定とのずれが解消されることがある。

クラシックカートのショートコードとページ設定を再確認する

クラシックカートのショートコードとページ設定を再確認する

クラシックカートとチェックアウトのページに正しいショートコードが貼られているかも併せて確認する。カートページには[woocommerce_cart]、チェックアウトページには[woocommerce_checkout]が必須だ。

WooCommerceの「設定」→「詳細設定」タブにある「カートページ」と「チェックアウトページ」の指定が、実際にショートコードを貼ったページと一致しているか確認する。別のページを指定したままだと、表示されている地図ページとStripeの初期化対象ページがずれて、ボタンが出ないことがある。

ページビルダー(ElementorやBeaver Builderなど)でカートページを作成している場合は、ショートコードウィジェットを配置しているか確認する。ページビルダーのテキストブロックにショートコードを直接入力していると、WooCommerceのテンプレートフックが正しく読み込まれず、StripeのExpress Checkoutボタン用のフックが実行されないことがある。

キャッシュとCDNを削除して再テストする

キャッシュとCDNを削除して再テストする

設定変更後もボタンが表示されない場合は、サーバー側のキャッシュとCDNのキャッシュが古いスクリプトを配信し続けている可能性がある。WooCommerce専用のキャッシュプラグインを使っている場合は、そのキャッシュを全削除する。CDN(コンテンツデリバリネットワーク / 配信網)を利用している場合は、CDNのダッシュボードからキャッシュをパージする。

その後、シークレットウィンドウ(プライベートブラウジング)でクラシックカートのページを開いて確認する。通常のブラウザに残っているクッキーやサービスワーカーが古いスクリプトを参照していることがあるため、シークレットウィンドウで確認するとキャッシュの影響を排除できる。

それでも直らない場合、一時的にすべてのプラグインを無効化して標準テーマに切り替え、WooCommerceとStripeゲートウェイだけを有効化した状態でテストする。この最小構成でボタンが表示されれば、別のプラグインかテーマが原因だ。影響しているプラグインを一つずつ有効化して切り分けていく。

よくある質問

商品ページではボタンが出るのにクラシックカートでは出ないのはなぜか

商品ページとクラシックカートではExpress Checkoutボタンを初期化するJavaScriptのフックが異なる。商品ページはWooCommerce標準のフックで動くが、カートとチェックアウトはページテンプレートの構造に依存するため、テーマやプラグインの競合で初期化が妨げられることがある。

ブロックカートに切り替えれば解決するのか

ブロックカートとブロックチェックアウトはGutenbergブロックとしてレンダリングされるため、Stripeゲートウェイが専用コンテナを確実に配置でき、ボタンが正常に表示される。どうしてもクラシック版を使い続ける必要がなければ、ブロック版への移行は有効な回避策になる。

Stripeアカウントの再接続だけでは直らない場合はどうするか

再接続で直らない場合は、WooCommerceのStripe設定画面でExpress Checkoutボタンの表示オプションを一度すべてオフにして保存し、再度オンにして保存する。これでプラグインの設定値がリフレッシュされ、クラシックページ用のフックが再登録されることがある。

ログに出るホスト型決済方法設定のエラーは無視してよいか

無視しないほうがよい。ホスト型決済方法設定への切り替えエラーは、Stripe側の設定とWooCommerceプラグインの設定が同期していない状態を示している。この状態を放置すると、クラシックページでのボタン非表示だけでなく、支払い処理自体に影響が出る可能性がある。Stripeダッシュボードで決済方法設定を確認し、手動設定に切り替えることを推奨する。

スマートフォンでもボタンが表示されないのは同じ原因か

基本的には同じ原因だ。ただしスマートフォンではApple PayとGoogle Payの表示条件が端末の対応状況にも左右される。iPhoneならSafari、AndroidならChromeで、ウォレットにカードが登録されていないとボタン自体が表示されない。パソコンで表示されることを前提に原因を切り分けるほうが正確だ。

この記事のポイント

  • クラシックカートとチェックアウトだけボタンが出ないのはStripeスクリプトの初期化タイミングのずれが主因
  • デベロッパーツールのコンソールでJavaScriptエラーを確認する
  • Stripeダッシュボードのホスト型決済方法設定を手動設定に切り替える
  • ショートコードの貼付位置とWooCommerceの設定ページ指定が一致しているか確認する
  • キャッシュとCDNを削除し、シークレットウィンドウで再テストする
WordPressでHTTPS切替後に画像やメニューが崩れる時の直し方

WordPressでHTTPS切替後に画像やメニューが崩れる時の直し方

WordPressサイトをHTTPからHTTPSへ切り替えた直後に、メニューが反応しない、画像ギャラリーが表示されない、ページレイアウトが崩れるといった症状が出る場合は、データベース内に残った「http」形式のURLが原因だ。Better Search Replaceというプラグインで旧URLを一括置換し、ElementorのCSSを再生成すれば解消できる。

なぜHTTPS切替後にサイト表示が崩れるのか

なぜHTTPS切替後にサイト表示が崩れるのか

HTTPSへ切り替えただけでは、WordPressのデータベースに保存された古いURLは自動的に更新されない。投稿本文やメタ情報、ウィジェット設定、ElementorのCSSファイル内に「http」形式のURLが残ったままになるためだ。

ブラウザはHTTPSページの中にHTTPの画像やスクリプトが混在している状態を「混合コンテンツ」と呼び、セキュリティ上の理由で読み込みをブロックする。この結果、画像が途中で消える、メニュークリックが反応しない、ギャラリーのスライドが止まるといった症状が起きる。

Before(HTTP残存)
ページURLは HTTPS なのに
画像URLは HTTP のまま
→ ブラウザが画像をブロックして表示が崩れる
After(HTTPS統一)
ページURLは HTTPS
画像URLも HTTPS
→ すべてのリソースが読み込まれて正常表示
HTTP残存(エラー状態)  HTTPS統一(修正後)

HTTPとHTTPSが混在するとブラウザがリソースをブロックする仕組みのデモ。

Better Search Replaceでデータベースを一括置換する手順

Better Search Replaceでデータベースを一括置換する手順

壊れたURLを手作業で直す必要はない。Better Search Replaceという無料プラグインを使えば、データベース内のすべての「http」形式のURLを「https」形式に一括で置換できる。事前に必ずバックアップを取っておくこと。

STEP 1 サイト全体のバックアップを取る
STEP 2 Better Search Replaceをインストールして有効化
STEP 3 検索に旧URL、置換に新URLを入力
STEP 4 すべてのテーブルを選択して置換を実行

Better Search ReplaceによるURL置換の全体の流れのデモ。

バックアップが最優先だ

置換操作はデータベース全体に影響を与えるため、失敗すると復旧が難しくなる。必ずプラグインやサーバー側のバックアップ機能で、データベースとファイルの両方を取得してから作業する。

検索と置換のURL指定を間違えない

検索フィールドには「http、自サイトのドメイン」、置換フィールドには「https、自サイトのドメイン」を入力する。ドメインの前後やスラッシュの有無を間違えると置換が正しく行われないため、コピーアンドペーストで正確に入力するのが安全だ。

置換後の確認ポイント

置換が完了したら、ブラウザのシークレットモードでサイトを開き、画像やメニューが正常に表示されるか確認する。管理画面の「設定 → 一般」に記載されたURLもHTTPSになっているか合わせてチェックする。

WordPress設定とElementorを修復する手順

WordPress設定とElementorを修復する手順

データベースの置換後も、WordPressの「一般設定」に記載されたサイトURLとWordPress URLが正しいHTTPS形式になっているか確認する必要がある。管理画面にログインできる場合は、設定画面から変更するだけでよい。

管理画面に入れなくなった場合はphpMyAdminから修正する

URLを誤って書き換えてログインできなくなった場合は、サーバーの管理画面からphpMyAdminを開き、wp_optionsテーブルの「siteurl」と「home」の2つの値を正しいHTTPSのURLに戻す。これでWordPressに再ログインできる。

ElementorのCSSを再生成する

ElementorはCSSを自動生成して保存しているため、URL変更後はこのCSSが古いHTTPのURLを参照したままになることがある。Elementorの設定画面から「CSSを再生成」を実行し、合わせて「Elementorのデフォルト設定を更新」も確認する。

Elementor CSS再生成の実行画面
管理画面の「Elementor → ツール」を開く
「CSSを再生成」ボタンをクリック
その後「Elementorのデフォルト設定を更新」も実行
Elementor 修復後の状態
生成されたCSSがすべて HTTPS を参照
レイアウトが正常に表示される

ElementorのCSS再生成の手順と修復後の状態のデモ。

置換しても直らない場合の追加対策

置換しても直らない場合の追加対策

一括置換後にキャッシュやサーバー設定が原因で症状が残ることがある。順番に確認していくことで、残りの問題を特定できる。

ブラウザのデベロッパーツールで混合コンテンツを探す

ChromeやEdgeのデベロッパーツール(F12)を開き、コンソールタブを確認する。「Mixed Content」という警告が出ていれば、まだHTTPのURLが残っている。該当するURLをメモし、Better Search Replaceで追加の置換をかける。

キャッシュ系プラグインとサーバーキャッシュを削除する

キャッシュプラグイン(WP Super CacheやW3 Total Cacheなど)が古いHTTPのページを保存していると、データベースを置換しても表示が変わらない。プラグインの設定からキャッシュを全削除し、あわせてサーバー側のキャッシュも管理画面からクリアする。

htaccessでHTTPをHTTPSへリダイレクトする

WordPressサイトのルートにあるhtaccessファイルに、HTTPアクセスをHTTPSへリダイレクトする設定を追加する。これにより、古いHTTPのURLにアクセスしても自動的にHTTPSへ転送され、検索エンジン評価の分散も防げる。不安であればサーバー会社のサポートに設定を依頼してもよい。

よくある質問

Really Simple SSLプラグインでは直らないのか

Really Simple SSLはHTTPSへのリダイレクトと、管理画面のURL更新を自動で行うプラグインだ。ただし、データベース内のすべてのHTTP URLを置換する機能は制限版では一部に限られる。画像やElementorのCSSに残るURLを完全に直すには、Better Search Replaceによる一括置換が必要になる。

置換後にログインできなくなったらどうするか

URL設定を誤るとログアウトして再ログインできなくなることがある。その場合はphpMyAdminからwp_optionsテーブルの「siteurl」と「home」を正しいHTTPSのURLに戻す。これで管理画面に再アクセスできる。

置換しても一部の画像が表示されない場合は

置換後の画像不表示は、キャッシュが古いHTTPのページを返しているか、CDNや外部サービスが元のURLを参照している可能性がある。キャッシュを全削除し、CDNを利用している場合はCDN側のキャッシュもクリアする。

HTTPS化後のSEOへの影響はあるのか

HTTPS化はGoogleのランキングシグナルとして評価されるため、正しくリダイレクトを設定すればSEOにはプラスに働く。サイト側でHTTPとHTTPSが両方アクセスできる状態を放置すると評価が分散するため、リダイレクト設定まで済ませておく。

Elementorで編集画面が真っ白になった場合は

URL変更後にElementorの編集画面が読み込めない場合は、CSS再生成とあわせて「Elementorのデフォルト設定を更新」を実行する。さらにWordPressのパーマリンク設定を一度「保存」して書き換えることで、内部リンク構造がリフレッシュされる。

この記事のポイント

  • HTTPS切替後もデータベースにHTTPのURLが残り、混合コンテンツとしてブロックされる
  • Better Search Replaceでhttpからhttpsへ一括置換する
  • 置換前に必ずフルバックアップを取る
  • ElementorはCSS再生成とデフォルト設定の更新が必要
  • キャッシュ削除とリダイレクト設定で仕上げる
WooCommerceのバリエーション商品だけ検索にヒットしない時の原因と直し方

WooCommerceのバリエーション商品だけ検索にヒットしない時の原因と直し方

WooCommerceで複数のバリエーションを持つ商品のうち特定のバリエーションだけサイト内検索に表示されない場合、Algoliaなどの検索インデックスに「非公開扱い」「価格未設定」「在庫切れの非表示」などの条件が記録されたままになっている可能性が高い。まず商品編集画面で該当バリエーションの有効状態とカタログ表示の設定を確認し、再保存してインデックスを更新する。

なぜ特定のバリエーションだけ検索にヒットしないのか

なぜ特定のバリエーションだけ検索にヒットしないのか

WooCommerceはバリエーションを親商品とは別の商品データとして管理する。検索用のインデックスを作る仕組みでは、親商品に加えて各バリエーションが別レコードとして登録されることが基本になる。その際、各バリエーションの公開条件や商品データに不備があると、そのバリエーションだけインデックスから除外される。

たとえば一部のバリエーションは検索に出るのに特定の型番だけ出ないという症状は、インデックス全体の問題ではなく、該当バリエーションの扱いに原因が集中している場合が多い。親商品の更新では直らず、バリエーション単位の設定を見直す必要がある。

WooCommerceでバリエーションの公開設定を確認する手順

WooCommerceでバリエーションの公開設定を確認する手順

バリエーションが検索に引っかからない場合、まず商品編集画面から該当バリエーションを開き、公開状態とデータの入力を確認する。単純商品であれば「更新」ボタンを押すだけで直る症状でも、バリエーション商品は個別の設定がそのまま残ることがある。

STEP 1 商品編集画面で「バリエーション」タブを開く
STEP 2 該当バリエーションを開き「有効」を確認する
STEP 3 価格、在庫、SKUの入力を確認する
STEP 4 商品を更新して検索インデックスを再構築する
確認ステップ 設定とデータの修正

このデモでは、検索に出ないバリエーションを確認する流れを示している。ここからは各ステップの詳細を見ていく。

「有効」チェックボックスがオフになっていないか

バリエーション編集画面の上部には「有効」のチェックボックスがある。ここがオフになっていると、そのバリエーションはカタログや検索から除外される。特定のバリエーションだけ検索に出ない場合、最初に確認したい項目だ。

チェックが外れている場合はチェックを入れて保存する。複数のバリエーションで同じ問題が起きているなら、一覧画面の絞り込み表示を活用し、無効化されたバリエーションだけを確認するのが早い。

価格と在庫が正しく入力されているか

バリエーションに価格が入力されていない場合、WooCommerceはそのバリエーションを非表示にする。また在庫切れでかつ「在庫切れ商品をカタログから隠す」設定が有効だと、検索インデックスからも外れる。価格と在庫はバリエーションごとに独立して管理されるため、親商品が正常でも特定のバリエーションだけ欠けていることがある。

親商品の「カタログ表示」設定が検索対象になっているか

親商品の編集画面には「カタログ表示」の設定がある。ここが「ショップと検索結果」または「検索結果のみ」になっていないと、その商品のバリエーション全体が検索に出なくなる。ただし一部のバリエーションはヒットするというケースでは、親商品の設定は原因になりにくい。それでも親商品が「非表示」になっていないか併せて確認しておく。

Algoliaにバリエーションが同期されない場合の対処

Algoliaにバリエーションが同期されない場合の対処

WooCommerceとAlgoliaを連携しているサイトでは、商品データの変更がAlgolia側に正しく同期されないと、特定のバリエーションだけ検索結果から漏れることがある。管理画面からインデックス設定と同期キューを確認する。

変動商品のインデックス設定を確認する

AlgoliaのWooCommerce連携プラグインには、バリエーションをインデックスに含めるかどうかの設定がある。ここが無効だと親商品だけが検索対象になり、バリエーションの型番検索が機能しない。設定を開き、バリエーションが検索可能になっているか確認する。

該当バリエーションを無効化して再度有効化する

検索に出ないバリエーションの「有効」チェックを一度外して保存し、その後に再びチェックを入れて保存する方法が有効なことがある。これによりWooCommerce側の更新イベントが発火し、Algoliaへの同期処理が再実行される。単純商品の「更新」で直るのと同じ理屈だが、バリエーションは親商品の更新だけではイベントが伝わらない場合がある。

インデックスキューをリセットして再構築する

Algolia連携プラグイン側でキューに滞留しているタスクがあると、一部の商品だけ同期されない。プラグインの設定画面からインデックスを再構築するか、キューをクリアしてから商品を再保存する。再構築には数分かかることがあるため、完了後に検索結果を確認する。

再保存しても直らない時に確認するデータ不整合

再保存しても直らない時に確認するデータ不整合

設定面で問題がないのにインデックスされない場合は、バリエーションデータそのものに不整合が起きている可能性がある。データベース上のpost_statusやSKUの重複を確認する。

重複するSKUや空の価格が原因になっていないか

SKUが別の商品と重複していると、検索プラグイン側でレコードの上書きや取り込みスキップが発生することがある。また親商品で価格を設定せずバリエーション側だけ価格を入れる運用では、空の値が検索条件から除外される場合もある。SKUの重複は管理画面の商品一覧からCSV書き出しなどで確認できる。

バリエーションの投稿状態が下書きになっていないか

WooCommerceのバリエーションは内部的に「product_variation」という投稿タイプで保存される。何らかの操作で投稿状態が下書きのままになっていると、検索インデックスに登録されない。通常は管理画面から確認できないため、WP CLIやデータベースの直接確認が必要になる。WP CLIが使える環境なら次のコマンドで該当バリエーションの状態を調べられる。

wp post list --post_type=product_variation --post_status=draft

検索インデックスを更新したあとに確認する動作

検索インデックスを更新したあとに確認する動作

設定とデータを修正したら、実際にサイトの検索窓から該当するバリエーションを検索して表示されるか確認する。検索結果に出るかだけでなく、リンク先のURLが正しく該当バリエーションに飛ぶかも見る。

フロントエンドで検索して該当バリエーションが出るか

型番やSKUを検索語にして、対象のバリエーションがヒットするか確かめる。キャッシュが残っていると更新前の結果が表示されることがあるため、ブラウザのシークレットウィンドウを使うか、サイト側のキャッシュを削除してから検索する。

検索結果のURLが正しいバリエーションに飛ぶか

バリエーション検索では、インデックスから返されるURLにバリエーション識別子が含まれていないと、親商品のページに飛んで該当するオプションが初期表示されない。検索結果をクリックして商品ページを開き、狙った型番が選択された状態で表示されるか確認する。URLに属性のクエリパラメータが付いているかを目視で確かめるのが確実だ。

よくある質問

バリエーション検索はWooCommerce標準機能でも使えるのか

WooCommerce標準の商品検索は親商品を対象にすることが多く、バリエーション単位の型番検索には対応が弱い。Algoliaなどの専用検索プラグインを入れることで、バリエーションごとのインデックスが可能になる。

Algoliaの再インデックスにかかる時間はどのくらいか

商品数が多いほど時間がかかる。数百件程度なら数分で完了するが、数千件を超える場合は10分以上かかることもある。完了後に何度か検索して結果が安定するのを確認する。

特定のバリエーションだけ在庫切れになるたびに検索から消えるのはなぜか

WooCommerceの設定で「在庫切れ商品をカタログから隠す」が有効になっていると、在庫がゼロになったバリエーションは検索インデックスから除外される。在庫切れでも検索に表示したい場合は、この設定を無効にするか、検索プラグイン側で在庫切れの扱いを調整する。

親商品を更新してもバリエーションが同期されないのはなぜか

Algolia連携プラグインによっては、親商品の更新イベントがバリエーションの個別更新まで伝わらないことがある。該当バリエーションを直接開いて保存するか、無効化と有効化の操作を行うことで同期が再開される。

バリエーションの一括修正はできるのか

商品一覧のCSV書き出しと取り込みを使うと、複数のバリエーションに対してSKUや価格、在庫を一括で修正できる。ただしインデックスへの同期までは自動で走らないことがあるため、取り込み後に再インデックスを実行する。

この記事のポイント

  • 特定のバリエーションだけ検索に出ない原因は、公開状態や価格、在庫といったバリエーション単位の設定に集中している
  • 商品編集画面で該当バリエーションを開き、「有効」チェックと価格、在庫を確認する
  • 親商品の更新だけでなく、該当バリエーションを一度無効化して再度有効化すると同期が再開されることがある
  • Algolia連携プラグインではインデックス設定と同期キュー、再構築の状態を確認する
  • それでも直らない場合はSKU重複やpost_statusの不整合を調べる
WordPressの__GA_INJ_START__マルウェア感染を完全駆除する手順

WordPressの__GA_INJ_START__マルウェア感染を完全駆除する手順

WordPress のテーマファイル functions.php に「__GA_INJ_START__」というコメント記述を見つけたら、Google Analytics を装ったマルウェア感染の可能性が高い。完全に駆除するには、functions.php を元に戻すだけでは不十分で、データベースに潜む隠し管理者アカウントの削除と侵入経路の遮断まで行う必要がある。

__GA_INJ_START__マルウェアとは何か

__GA_INJ_START__マルウェアとは何か

このマルウェアは、テーマの functions.php 内に不正なコードを注入する際、開始位置の目印としてコメント文「__GA_INJ_START__」を書き込む。Google Analytics の計測タグに似せた外見のため、コードをざっと見ただけでは正規のトラッキングコードと勘違いしやすい。

実際に注入されるコードは、Google Analytics とは無関係の永続的なバックドアとして働く。具体的には、不正な管理者アカウントを定期的に生成したり、攻撃者が自由にサイトへ再侵入するための隠し経路を維持する機能を持つ。コード自体が自己修復的に動くこともあり、単に該当部分を削除しても再び書き戻されるケースがある。

感染の典型的な流れは、まず正規の WordPress 管理者アカウントへ何らかの方法でログインし、管理画面内のファイル編集機能やコードスニペット系プラグインを経由して functions.php に到達する。その段階で不正な管理者アカウントを追加し、数日から数週間かけて隠しアカウントを増やした後、最終段階として __GA_INJ_START__ 付きのコードがテーマに注入される。

典型的な感染の進行フロー
正規のWP管理者ログイン ファイル編集系プラグイン操作 functions.phpへアクセス 不正な管理者アカウント作成 GA_INJテーマ注入
侵入段階  攻撃準備段階  最終的な感染完了

このデモは侵入から感染完了までの典型的な進行パターンを示している。攻撃者は一度管理者権限を得ると、すぐに目立つ改ざんを行うのではなく、まず隠しアカウントを作って持続的なアクセスを確保するのが特徴だ。

隠し管理者アカウントをどうやって見つけるか

隠し管理者アカウントをどうやって見つけるか

このマルウェアに感染したサイトでは、データベース内に通常では一覧に表示されない形で不正な管理者アカウントが追加されている。管理画面のユーザー一覧に表示されない場合もあるため、phpMyAdmin などでデータベースを直接確認するのが確実だ。

まず wp_users テーブルを開き、ユーザー名に不審な接頭辞が付いていないかを確認する。具体的には sync_agent、cdn_worker、seo_service の後にランダムな英数字が続く形式のアカウントが典型的だ。テーブルプレフィックスが wp_ 以外の場合は、その文字列に読み替えて探す。

次に wp_usermeta テーブルで、該当ユーザー ID に administrator 権限を付与するエントリが存在するかを調べる。さらに wp_options テーブルには __ga_hidden_users、_theme_inject_status、__ga_r_cache という見慣れないキーが保存されていることがある。これらのキーはマルウェアが隠し管理者の一覧を管理するために使う。

サーバーに SSH でログインできる環境なら、ファイルシステム全体を横断検索するのが最も早い。以下のコマンドでマルウェア特有の文字列を探せる。

grep -RniE '__GA_INJ|__ga_hidden_users|__ga_r_cache|_theme_inject_status|sync_agent|cdn_worker|seo_service' .

SSH が使えない場合は、FTP でファイルをダウンロードしてエディタの検索機能を使うか、運営中のレンタルサーバーが提供するファイルマネージャの検索機能を活用する。また、WordPress 管理画面から有効化されているプラグイン一覧を確認し、心当たりのないプラグインが増えていないかも必ず調べる。

マルウェアを完全に駆除するにはどうすればいいか

マルウェアを完全に駆除するにはどうすればいいか

重要なのは、functions.php を置き換えるだけでは駆除できないという点だ。隠し管理者アカウントとバックドアをすべて取り除くまで、攻撃者は何度でも再侵入できる。以下に駆除の全体フローを示す。

STEP 1 サイト全体のバックアップを取得する
STEP 2 データベースで隠し管理者アカウントを特定して削除する
STEP 3 functions.php から不正コードを除去する
STEP 4 全ファイルをバックドアの兆候がないか走査する
STEP 5 全管理者パスワードを変更しセッションをリセットする
STEP 6 WordPress本体、テーマ、プラグインを全て更新する

このデモは駆除作業の全体像を示している。以下、各ステップの具体的な進め方を詳しく解説する。

バックアップを必ず先に取得する

駆除作業ではデータベースのレコード削除やファイルの書き換えを行うため、操作を誤るとサイトが壊れる恐れがある。作業前にデータベースとファイルの両方を丸ごとバックアップしておく。レンタルサーバーにバックアップ機能が付属している場合も、念のため別の場所にもコピーを保存する。

隠し管理者アカウントをデータベースから削除する

phpMyAdmin などで wp_users テーブルを開き、不審なユーザー名のレコードを特定する。sync_agent、cdn_worker、seo_service にランダムな英数字が付いた形式が典型だが、まったく別の名前で偽装している可能性もある。新規登録した覚えのない管理者権限ユーザーはすべて削除対象だ。

ユーザーを削除する際、wp_usermeta テーブルに残った関連エントリも忘れずに削除する。SQL を直接実行する場合は、該当ユーザー ID を指定して両テーブルからレコードを消す。操作前に必ずバックアップを取り、プレビュー画面で対象レコードを確認してから実行する。

functions.php の不正コードを除去する

テーマの functions.php をエディタで開き、__GA_INJ_START__ から始まるコメントと、それに続くコードブロックを特定する。__GA_INJ_END__ または類似の終了マーカーがある場合は、その範囲全体を削除する。マーカーが無い場合は、不審な関数定義や管理者アカウントを操作するコードを丁寧に確認しながら取り除く。

該当テーマが親テーマなら、修正がテーマ更新で失われないよう子テーマ化を検討する。また、他のテーマファイルや wp-content 直下の PHP ファイルにも同じマーカーが仕込まれている可能性があるため、前述の grep コマンドで全ファイルを横断検索してから作業するのが安全だ。

セッションとパスワードをリセットする

攻撃者が既存のセッションを保持していると、アカウントを消してもアクセスが続く。WordPress の管理画面からユーザー一覧を開き、すべての管理者ユーザーに対して「全てのセッションを破棄」を実行する。さらに全管理者のパスワードを新しいものへ変更する。可能ならメールアドレスも再確認し、見覚えのない転送設定が無いか調べる。

アクセスログから感染時期を特定するにはどうするか

アクセスログから感染時期を特定するにはどうするか

__GA_INJ_START__ が functions.php に現れた日が感染開始日とは限らない。実際には、その数日前から数週間前にかけて攻撃者が隠し管理者アカウントを作り、段階的に足場を固めていたケースが多い。駆除後に再発を防ぐには、感染の起点となった脆弱性や認証情報を特定することが欠かせない。

まずデータベースの wp_users テーブルで、不正な管理者の登録日時を確認する。WordPress はユーザー作成日時を user_registered カラムに記録している。次にサーバーのアクセスログを同じ期間分さかのぼり、wp-login.php へのログイン試行や、admin-ajax.php、theme-editor.php などへの不審なアクセスが無いかを照合する。

攻撃者が最初に正規の管理者アカウントでログインしていた場合、ログには正常なログインとして記録されているため見落としやすい。ログイン元 IP アドレスの突発的な変化、深夜帯のログイン、短時間での連続したファイル編集操作などを手がかりにする。ログの保存期間が短いレンタルサーバーでは、可能な範囲でログ保管期間を延ばしておくと今後の調査に役立つ。

再感染を防ぐには何をすればいいか

再感染を防ぐには何をすればいいか

駆除が完了しても、侵入経路が残っていれば同じ手口で再び感染する。再発防止には、まず WordPress 本体、テーマ、プラグインを最新版へ更新する。侵入経路として悪用された可能性のあるファイル編集系プラグインやコードスニペット系プラグインは、使用していないなら削除する。

管理者アカウントに対しては二段階認証を有効にし、パスワードは推測されにくい長いものへ変える。管理画面へのアクセスを IP アドレス制限で絞るのも効果的だ。さらに wp-config.php にファイル編集機能を無効化する定数 DISALLOW_FILE_EDIT を追加すると、管理画面からテーマやプラグインのコードを書き換えられる経路を塞げる。

定期的な点検も重要になる。ユーザー一覧に知らない管理者が増えていないか、wp_options に見覚えのないキーが無いか、functions.php などのテーマファイルに不審なコメントが追加されていないかを毎月確認する。可能ならセキュリティプラグインによる定期スキャンを導入し、変更検知の通知を受け取れるようにしておく。

Before 感染状態

functions.php に __GA_INJ_START__ が注入され、データベースには sync_agent や cdn_worker などの隠し管理者が存在する。攻撃者はいつでも再侵入できる状態。

After 駆除完了

不正コードが除去され、隠し管理者はデータベースから完全に削除済み。パスワードとセッションもリセットされ、更新も適用されている。

感染状態  駆除完了

このデモは駆除前後の状態を対比したものだ。感染状態ではマルウェアの目印と隠し管理者が残っているが、駆除後は不正な要素がすべて取り除かれている。

よくある質問

__GA_INJ_START__はGoogle Analyticsの正規コードではないのか

正規の Google Analytics 計測コードにこのようなマーカーは存在しない。__GA_INJ_START__ は不正なコードの開始位置を示す目印で、マルウェアが後からコードを書き戻す際の識別子として使われる。テーマファイルにこの文字列を見つけたら感染を強く疑うべきだ。

隠し管理者アカウントはデータベースのどこを確認すれば見つかるか

wp_users テーブルと wp_usermeta テーブルの両方を確認する。ユーザー名が sync_agent、cdn_worker、seo_service にランダムな英数字が付いた形式なら要注意だ。さらに wp_options テーブルに __ga_hidden_users や _theme_inject_status などの見慣れないキーが無いかも調べる。

セキュリティプラグインを導入しているのに感染したのはなぜか

プラグインが検出できるシグネチャを持たない新種や変種だった、定義が古かった、正規の管理者としてログインしてから活動したため不正ログインと判定されなかった、などの理由が考えられる。プラグインに頼るだけでなく、定期的なユーザー一覧やファイルの目視確認も併用する。

感染後、サイトを公開したまま駆除作業はできるか

推奨されない。攻撃者がバックドアを持っている間、サイトを公開し続けると訪問者の情報が窃取されたり、別の攻撃の踏み台にされたりする恐れがある。可能ならメンテナンスモードに切り替え、バックアップを取ってから作業するのが安全だ。

駆除後にサイトが真っ白になった場合の対処方法は?

デバッグモードを有効にしてエラー内容を特定し、テーマやプラグインを一つずつ有効化して切り分ける。テーマの functions.php を編集した際に記述ミスがあると画面が真っ白になることが多い。感染前のバックアップがあれば、その時点から修復する方が確実な場合もある。

この記事のポイント

  • __GA_INJ_START__はGoogle Analyticsを装ったマルウェアの目印
  • 隠し管理者アカウントはデータベースを直接確認しないと見落とす
  • functions.phpの置き換えだけでは再感染する
  • アクセスログを数日〜数週間さかのぼって感染起点を探す
  • 駆除後は全パスワード変更とセッションリセットが必須
Two-Factor 0.15.0で2FAコードが無効になる時の対処と原因

Two-Factor 0.15.0で2FAコードが無効になる時の対処と原因

Two-Factor プラグインを 0.15.0 に更新後、認証アプリのコードが「無効な確認コード」と拒否される場合、一時的に 0.14.2 へ戻すのが最も確実な対処だ。並行してサイト環境とプラグインの互換性を確認し、根本原因を切り分ける。

なぜ 0.15.0 で 2FA コードが無効になるのか

なぜ 0.15.0 で 2FA コードが無効になるのか

Two-Factor 0.15.0 では認証コード検証の内部処理が見直された。その結果、特定の環境で「それまで使えていたコード」が突然拒否される症状が報告されている。すべてのサイトで起こるわけではなく、PHP バージョンや共存プラグインの組み合わせが影響する。

典型的なエラーは「ERROR: Invalid verification code.」だ。日本語環境では「無効な確認コード」と表示されることが多い。認証アプリ側の時刻ずれではないのに毎回弾かれる場合、プラグイン側の検証処理が疑わしい。

0.14.2 では問題なくログインできていたなら、ユーザーが設定した秘密鍵そのものは生きている。鍵の保存形式やハッシュ計算の互換性が 0.15.0 で崩れた可能性が高い。

0.14.2 と 0.15.0 の認証フロー比較
0.14.2 保存済みの秘密鍵をそのまま検証 → ログイン成功
0.15.0 検証処理の変更により鍵が不一致 → 無効な確認コード
正常に動作  エラー発生

このデモは、バージョン更新前後の認証結果の違いを概念的に示したイメージだ。

まず 0.14.2 に戻してログインを復旧する手順

複数ユーザーが締め出されているなら、何より先にアクセスを回復する。0.15.0 を無効化し、0.14.2 を入れ直す手順を紹介する。

管理画面に入れる場合の戻し方

管理者自身はログインできる場合、プラグイン画面から操作できる。ただし 2FA が有効なサイトでは、管理者もログイン時にコードを要求される点に注意する。

  • 「プラグイン」→「インストール済みプラグイン」で Two-Factor を無効化する
  • プラグインを削除する
  • 「新規プラグインを追加」から Two-Factor を検索する
  • バージョン 0.14.2 をダウンロードしてインストールする

バージョンを指定してインストールするには、WordPress.org のプラグインページにある「詳細」画面下部の「旧バージョンをダウンロード」から取得できる。

管理画面に入れない場合の対処

管理者も含めて誰もログインできない場合、FTP またはサーバーのファイルマネージャーからプラグインフォルダーを操作する。

  • FTP で wp-content/plugins/ に接続する
  • two-factor フォルダーを一時的にリネームする(例 two-factor-old
  • ログイン画面から通常のパスワードのみで入れるようになる
  • その後、管理画面から 0.14.2 を再インストールする
ログイン復旧までの流れ
STEP 1 Two-Factor 0.15.0 を無効化またはリネーム
STEP 2 パスワードのみでログインできることを確認
STEP 3 0.14.2 をインストールして有効化

このデモは、管理画面に入れない状態から復旧するまでの手順を示している。

ログインできた後に確認すべき環境要因

ログインできた後に確認すべき環境要因

アクセスを回復したら、なぜ 0.15.0 だけが問題を起こすのかを切り分ける。同じプラグインを更新しても、環境によっては正常に動くケースがあるためだ。

PHP のバージョンと拡張

0.15.0 は PHP 8.4 系で問題が出た事例がある。一方、8.3 系で動いているサイトでは同じ更新が成功する報告もある。PHP のバージョンだけでなく、ハッシュ計算に関係する拡張機能の有無も差を生む。

レンタルサーバーの管理画面から PHP バージョンを確認し、可能なら 8.3 系へ一時的に切り替えて 0.15.0 の動作を試す。ただし、PHP を変更すると他のプラグインやテーマに影響するため、事前にバックアップを取ってから実施する。

WPML など多言語プラグインとの共存

複数ドメインで動かす WPML 構成では、認証に関係する URL やクッキーの扱いが変わる。0.15.0 でこれが悪さをした可能性も考えられる。WPML を使っているサイトで問題が再発するなら、Two-Factor と WPML の両方の設定を見直す。

認証アプリ側の時刻と再同期

「無効な確認コード」は時刻ずれでも起きる。認証アプリの「設定」から時刻の同期を行い、それでも 0.15.0 だけが通らない場合は時刻ずれではないと判断できる。

バージョン固定と更新タイミングの判断

バージョン固定と更新タイミングの判断

0.14.2 で問題が起きていないなら、修正版が出るまで 0.14.2 に固定するのが実務的だ。ただし、セキュリティプラグインの古いバージョンを長期間使い続けるのは望ましくない。公式の変更履歴とサポートフォーラムを確認し、修正版が出たら速やかに更新する。

プラグインの自動更新が有効だと、意図せず再び 0.15.0 に上がる恐れがある。更新を止めるには、プラグインの自動更新設定をオフにするか、サイト全体の更新管理を見直す。

更新判断の目安
推奨 0.14.2 で運用し、修正版のリリースを待つ
注意 自動更新をオフにして意図しない更新を防ぐ

このデモは、0.15.0 を避ける運用方法と注意点を整理したものだ。

よくある質問

0.15.0 で一部のユーザーだけログインできないのはなぜ?

ユーザーごとに秘密鍵の保存形式が異なる可能性がある。古いバージョンで作成された鍵と新しい検証処理の相性が悪く、特定のユーザーだけ弾かれることがある。

0.14.2 に戻してもユーザーに再設定してもらう必要はある?

通常は必要ない。0.14.2 に戻せば、以前作成した認証情報とアプリのコードがそのまま使える。再設定を求めるのは、認証情報が壊れている場合に限られる。

認証アプリのコードが「無効な確認コード」になる他の原因は?

サーバーと端末の時刻ずれ、秘密鍵の保存不備、キャッシュによる画面の不整合などが考えられる。まず認証アプリの時刻同期を行い、その後プラグインのバージョンを確認する。

0.15.0 の修正版はいつ出る?

リリース時期は未定だ。公式のプラグインページとサポートフォーラムの更新を確認する。修正版が出るまでは 0.14.2 固定が安全だ。

この記事のポイント

  • Two-Factor 0.15.0 で 2FA コードが無効になる問題が報告されている
  • まず 0.14.2 に戻してログインを復旧する
  • PHP バージョンや WPML など環境要因を切り分ける
  • 修正版が出るまでは 0.14.2 固定と自動更新オフで運用する
WooCommerceのStripe SEPA決済が後日失敗する時の注文ステータス更新対処

WooCommerceのStripe SEPA決済が後日失敗する時の注文ステータス更新対処

WooCommerceのStripe決済でSEPAダイレクトデビットを利用する場合、完了ステータスのままだと後日の支払い不成立を注文に反映できない。SEPAは数日後に引き落としが確定する支払い方式なので、注文は保留中で開始し、StripeのWebhook通知でステータスを同期するのが基本だ。

SEPAダイレクトデビットはなぜ後日失敗するのか

SEPAダイレクトデビットはなぜ後日失敗するのか

SEPA(Single Euro Payments Area)のダイレクトデビットは、欧州の銀行口座から代金を引き落とす後日確定型の支払いだ。日本の口座振替に近く、注文時点では「引き落としの依頼」が受理されただけであり、銀行口座から実際に資金が移動したわけではない。

このため注文確定直後はStripeの決済ステータスが「処理中」や「成功」と表示されることがあっても、数日から数週間後に銀行が引き落としを実行した段階で残高不足や口座相違、口座閉鎖、顧客の同意撤回などが判明し、その時点で初めて支払い失敗が確定する。

つまりSEPAでは「注文時に成功していたように見える」状態と「実際の入金が完了した」状態が時間差で分かれる。この時間差が、後から失敗した注文を見つけにくくする根本的な要因だ。

STEP 1 顧客がSEPAダイレクトデビットで注文を確定する
STEP 2 Stripeが決済処理中と表示し、WooCommerceは注文を完了にする
STEP 3 数日から数週間後、銀行の引き落としが実行される
結果 残高不足や口座相違で銀行が拒否し、Stripe上は失敗になる
支払い失敗に切り替わる  WooCommerce側は完了のまま残ってしまう

この流れで後日失敗が起きても、WooCommerceの注文は完了のまま残るため、店舗側からは売上確定後に消える注文のように見える。

WooCommerceの注文ステータスはなぜ変わらないのか

WooCommerceの注文ステータスはなぜ変わらないのか

一番の原因は、WooCommerceの注文ステータス「完了」が終端状態である点だ。完了になった注文は、標準の状態遷移では処理中や保留中へ戻せない。後からStripeが支払い失敗を通知しても、すでに完了になっている注文を自動で失敗に切り替える動作は発生しない。

StripeプラグインはWebhookイベントを受け取って注文ステータスを更新する。しかしWebhookで支払い失敗を受け取っても、対象の注文が完了の場合、状態遷移がブロックされる。逆に注文が保留中や処理中のままなら、失敗ステータスへ更新できる余地がある。

加えて、Webhookエンドポイントの設定漏れやシークレットキーの不一致があると、Stripeからの通知そのものがWooCommerceに届かない。この状態では支払い失敗だけでなく、成功通知も正しく反映されなくなる。

Before(完了のまま運用)
注文を完了にして発送まで進める
数日後にStripeが支払い失敗を通知する
完了は終端状態のため注文ステータスが変わらない
After(保留中から開始する運用)
注文を保留中にする
Stripeが支払い失敗を通知する
保留中なら注文を失敗ステータスに変更できる
完了のままでは通知を受けても動かない  保留中なら後日の失敗を反映しやすい

このように、WooCommerceの注文ステータスは「完了」を途中で覆す前提で設計されていない。SEPAのような後日確定の支払いでは、注文ステータスを保留中に保つのが安全だ。

後日失敗を検知するにはどう設定すればよいか

後日失敗を検知するにはどう設定すればよいか

対策は大きく2つある。1つはStripeとWooCommerceの間でWebhook通知を正しく受け取れる状態にすること、もう1つはSEPA注文の初期ステータスを保留中にして後から失敗に変更できる余地を残すことだ。

STEP 1 StripeダッシュボードでWebhookエンドポイントを追加する
STEP 2 WooCommerceのStripe設定にWebhookシークレットを登録する
STEP 3 SEPA注文の初期ステータスを保留中に変更する
STEP 4 入金確定後にだけ手動で完了へ進める運用にする
Stripe側の通知設定  WooCommerce側の受け取り設定  注文の初期ステータス変更

上記の流れを1つずつ設定していけば、後日失敗した注文をWooCommerce側で検知しやすくなる。

Stripe側でWebhookエンドポイントを新規作成する

Stripeのダッシュボードで「開発者」→「Webhooks」を開き、エンドポイントを追加する。エンドポイントURLにはWooCommerceが用意する受信先(通常は example.com/?wc-api=wc_gateway_stripe に相当する自サイトのアドレス)を指定する。イベントには payment_intent.processing、payment_intent.payment_failed、charge.failed を最低限有効にする。

WooCommerce側にWebhookシークレットを登録する

WooCommerceの管理画面で「WooCommerce」→「設定」→「決済」→「Stripe」を開き、詳細設定にあるWebhook Secret欄にStripeから発行された署名シークレットを貼り付ける。これはStripeからの通知が本物であることを検証する鍵で、一致しない場合はイベントが受け付けられない。

SEPA注文の初期ステータスを保留中にする

WooCommerceのStripe決済設定には、支払い方法ごとに注文ステータスを割り当てる項目がある。SEPAダイレクトデビットを「処理中」または「保留中」にしておくと、支払い失敗時の自動更新が効きやすい。完了への変更は入金確定後に行う。

自動で完了にする運用をやめる

注文を一括で完了に変更する自動化プラグインやカスタムコードを使っている場合、SEPA注文には適用しないよう除外する。対象を前払いのカード決済や銀行振込に限定し、後日確定の支払い方法は人による確認を挟む。

再発を防ぐには何を監視すべきか

再発を防ぐには何を監視すべきか

Webhookとステータス設定を整えても、銀行側の事情で通知が遅れたり不達になるケースは残る。店舗側ではStripeダッシュボードの支払い失敗一覧とWooCommerceの注文リストを週次で突き合わせる運用が現実的だ。

  • Stripeダッシュボードの支払い失敗イベントを定期確認する
  • WooCommerceのWebhookログにエラーが出ていないか確認する
  • 保留中のまま長期滞留している注文を抽出する
  • 完了済み注文の中から支払い失敗に切り替わったものを洗い出す

WooCommerceは「WooCommerce」→「システムステータス」→「ログ」にStripeのWebhook受信ログを残す。日付が新しいログに対象注文が見つからないなどのエラーが記録されている場合は、該当する注文の状態を手動で修正する。自動化に頼りきらず、最終確認として人の目を挟むのが安全だ。

よくある質問

SEPAの支払いが失敗した場合、顧客には自動で通知される?

Stripeからは顧客へメール通知が送られるが、WooCommerce側の注文メールはステータス変更を基に動作する。失敗を検知したら店舗側から個別に連絡する運用にしておくと、顧客対応が安定する。

StripeのWebhookが受信されているか確認するには?

WooCommerceのシステムステータスにあるログ一覧でStripe Gatewayのログを開く。日付が新しいログにエラーがなければ受信できている。不安な場合はStripeダッシュボードでテストイベントを送信して確認する。

既に完了にしている注文を後から失敗に変更できるか?

WooCommerceの標準画面では、完了から失敗への直接変更はできない。手動で注文ステータスドロップダウンから変更を試みても反映されないことが多い。必要な場合は対象注文を返金処理するか、カスタムコードでステータスを書き換えることになる。

SEPA以外の後払い決済でも同じ問題は起きる?

後日確定型の支払い方法であれば同様の問題が起きる。具体的には銀行振込や口座振替、一部の後払いサービスなどが該当する。支払い方法ごとに注文ステータスの初期値を確認しておくとよい。

Stripeの公式プラグイン以外でもWebhook設定は必要?

他のStripe連携プラグインでも、Stripeのイベントを受け取る仕組みが備わっている。プラグインの設定画面にWebhook URLやシークレットキーの項目があるかを確認し、なければ手動でエンドポイントを追加する。

この記事のポイント

  • SEPAダイレクトデビットは後日確定するため注文時の成功は最終結果ではない
  • WooCommerceの完了ステータスは終端状態で失敗へ自動変更できない
  • 注文は保留中で開始し入金確定後にのみ完了へ移す
  • StripeのWebhookとWooCommerceのシークレット設定を同期させる
  • StripeダッシュボードとWooCommerceのログを定期的に突き合わせる
WordPressのmu-pluginsに潜むPopCashマルウェアを見つけて削除する方法

WordPressのmu-pluginsに潜むPopCashマルウェアを見つけて削除する方法

WordPressサイトで意図しないタブが勝手に開くポップアンダー攻撃が続く場合、通常のプラグインではなく mu-plugins フォルダにマルウェアが隠れている可能性が高い。全プラグインを無効化しても症状が消えないなら、必須プラグイン領域とテーマ内の偽 JavaScript ファイルを点検し、該当ファイルを削除したうえで Thrive Architect を最新版へ更新する。

なぜマルウェアはプラグイン無効化後も残るのか

なぜマルウェアはプラグイン無効化後も残るのか

mu-plugins フォルダは「必須プラグイン」とも呼ばれ、/wp-content/mu-plugins/ に置いた PHP ファイルは自動的に読み込まれる。WordPress 管理画面からプラグインを一括停止しても、このフォルダの中身は対象外になるため、攻撃者はここにファイルを置くと症状を消さずに済む。

今回確認された手口では、テーマフォルダ内の js ディレクトリに PHP ファイルが置かれ、JavaScript として配信されていた。拡張子が .php のまま配信時の形式だけを JavaScript に偽装し、テーマの script タグから読み込まれる形を装う。

この PHP ファイルは外部の api-js.popcash.net にサーバー間通信でアクセスし、得たコードを訪問者のブラウザへそのまま流す。ローカルファイル自体には難読化や eval がなく、「広告ネットワークの API を呼び出すだけのコード」に見える。これが Wordfence や Sucuri などのスキャナーに検出されない理由だ。

設定にはポップアンダーを有効にする pop_fback というオプションが含まれる。さらに curl、shell_exec、file_get_contents へ順に切り替えるフォールバックを持つため、サーバー側の関数制限が厳しくても動き続ける。

mu-pluginsに潜む感染ファイルを見つける手順

mu-pluginsに潜む感染ファイルを見つける手順

調査は FTP または SSH でファイルを直接確認するのが確実だ。感染ファイルは管理画面から見えない場所に置かれるため、ブラウザ上のプラグイン一覧だけでは発見できない。

STEP 1 mu-pluginsフォルダの中身をFTPかSSHで確認する
STEP 2 ppckやqtt-など暗号めいた名前のPHPを探す
STEP 3 テーマ配下で.jsとして呼ばれるPHPファイルを探す
STEP 4 findコマンドで直近60日以内に変更されたPHPを洗い出す

感染ファイルを検出する調査フローを4ステップで示す。以下で各手順を詳しく説明する。

mu-pluginsフォルダ内の全ファイルを確認する

FTP アプリや SSH で /wp-content/mu-plugins/ を開き、ファイル名を目視で確認する。今回確認が取れているのは wp-ppck-assets.php という名前だが、同じ攻撃ツールは qtt-ppck-core.php や qtt-ajax-core.php といった別名でも置かれる。通常のサイト運営で作った覚えのない .php ファイルが1つでもあれば、削除候補として控えておく。

暗号めいた名前をキーワード検索する

ppck、qtt-、popcash といった文字列がファイル名やディレクトリ名に含まれていないか検索する。一見無害な英数字列に変えられたケースもあるため、名前だけで安全と判断せず、中身と更新日時も確認する。

テーマフォルダ内でPHPファイルが.jsとして呼ばれていないか確認する

テーマディレクトリの js フォルダや assets フォルダに、拡張子が .php のファイルが script タグで読み込まれる形跡がないか確認する。JavaScript の置き場所に PHP があること自体が不自然だ。今回のペイロードは qtt-ppck-core.php という PHP ファイルがテーマの js フォルダに置かれ、外部 API から取得したスクリプトを訪問者へ配信していた。

findコマンドで直近に変更されたPHPファイルを洗い出す

SSH が使える環境なら find コマンドで直近60日以内に変更された PHP ファイルを一覧化する。攻撃者は設置後に修正日時を偽装していないことが多いため、この一覧は感染日の特定に有効だ。

find /path/to/wordpress -type f -mtime -60 -name "*.php"

マルウェア本体とドロッパーを完全に削除する

マルウェア本体とドロッパーを完全に削除する

感染ファイルを特定したら、本体だけでなく侵入に使われたアップローダーも削除しなければ再感染する。ここでは削除対象と優先順位を整理する。

感染時(Before)
/wp-content/mu-plugins/wp-ppck-assets.php
/themes/テーマ名/js/qtt-ppck-core.php
/themes/テーマ名/js/_w10_up.php
削除後(After)
/wp-content/mu-plugins/ は空
テーマ内の.jsディレクトリにPHPが残っていない
感染時に存在するファイル  正常化後の状態

感染時に存在した不要ファイルと削除後の状態を対比する。削除対象は本体だけに留めない。

最初に該当ファイルをすべて削除する

mu-plugins 内の wp-ppck-assets.php と、テーマ内の qtt-ppck-core.php を削除する。同じツールキットは qtt-ajax-core.php や wp-tmp-up.php、_w10_up.php といった別名でも設置されるため、検索でヒットした全ファイルを対象にする。削除前には必ずバックアップを取り、削除後はサイトの表示と管理画面へのログインが正常にできることを確認する。

ルート直下の検証用テキストファイルも削除する

攻撃者は任意のファイル書き込みが可能か確認するために、WordPress のルートディレクトリへランダムな英数字20文字の .txt ファイルを置くことがある。今回の例では 52faade47ac664d8d0d3.txt というファイルが確認されており、削除対象になる。同様の .txt が残っていれば侵入テストの痕跡として除去する。

バックドアのパターンを全PHPから検索する

ファイルを消しただけでは、別の場所に置かれたバックドアが残る可能性がある。eval(、base64_decode(、gzinflate(、shell_exec(、assert( などの危険な関数が含まれる PHP を全検索する。正規のプラグインが使っている場合もあるため、検索結果はファイルの出所と更新日時を確認しながら判定する。

grep -Rl -e "eval(" -e "base64_decode(" -e "shell_exec(" /path/to/wordpress

Thrive Architectの脆弱性を塞ぐアップデート手順

Thrive Architectの脆弱性を塞ぐアップデート手順

今回の感染経路は Thrive Architect のクロスサイトスクリプティング脆弱性だった。プラグインを更新するだけでは設置済みのマルウェアは消えないため、削除作業の後に必ず更新する。

CVE-2026-66694の影響範囲

2026年8月6日に公開された CVE-2026-66694 は、Thrive Architect バージョン10.9.3.1以前に存在する未認証のクロスサイトスクリプティングと任意コード入力の脆弱性だ。自動化されたボットが未パッチのサイトをスキャンし、ファイル書き込み権限を取得してマルウェアを展開した。対象バージョンを使い続けると、同様の侵入が繰り返される。

最新版への更新と注意点

管理画面の更新画面または公式の入手経路から Thrive Architect をバージョン10.9.3.2以降へ更新する。更新によって脆弱性は塞がるが、すでにアップロードされたドロッパーやバックドアは自動的に削除されない。必ず先に感染ファイルの除去を行い、その後で更新する順番を守る。更新後は改めて不審な PHP が増えていないか確認する。

感染後の再発防止と全パスワード変更

感染後の再発防止と全パスワード変更

マルウェアの削除と脆弱性対策が完了しても、攻撃者が別の認証情報を持っていれば再侵入される。認証情報の変更とログの確認まで行って、初めて駆除は完了する。

全パスワードを必ず変更する

WordPress の管理者、FTP や SFTP、データベース、ホスティングコントロールパネルの全パスワードを変更する。特に FTP や SFTP の認証情報が流出していた場合、管理者パスワードを変えただけでは再侵入を防げない。使い回しのパスワードは避け、二要素認証が使える場所では必ず有効化する。

WordPress本体と全テーマ・プラグインを更新する

WordPress コア、テーマ、すべてのプラグインを最新版にする。使用していないプラグインやテーマは削除する。海賊版や未更新のテーマは既知の脆弱性を多く含むため、公式に配布されている正規品だけを使う。更新後は管理画面と公開ページの動作を確認する。

サーバーログで侵入日時と経路を確認する

アクセスログには攻撃者の痕跡が残っている。今回の例では、任意ファイル書き込みの検証としてルート直下にランダムな .txt が作成され、約30分後に隠しアップローダーへ POST が送られ、その3秒後に curl でペイロードの動作確認が行われていた。ログを感染日の前後で精査すると、同じ手口で侵入されていないか、他に不審なリクエストが残っていないかを確認できる。

よくある質問

mu-pluginsとは何か、普通のプラグインと何が違うのか

mu-plugins は WordPress の必須プラグインフォルダで、手動で .php ファイルを置くと自動的に読み込まれる。管理画面から停止できないため、すべてのプラグインを無効化する操作の対象にならない。普段使わない環境なら中身が空であることが多いが、攻撃者はこの盲点を狙って設置する。

なぜWordfenceやSucuriは検出できなかったのか

マルウェア本体が外部 API を呼び出すだけのシンプルな構成で、eval や base64_decode などの典型的な攻撃パターンを含まないため、シグネチャ検出に引っかからなかった。ローカルファイル自体は無害に見え、危険なコードは外部から動的に取得される。このため、手動でのファイル名確認と日時調査が欠かせない。

Thrive Architectを更新すればマルウェアは自動で消えるか

消えない。アップデートで脆弱性は塞がれるが、すでに書き込まれたドロッパーやバックドアはそのまま残る。先に怪しいファイルを削除してから更新し、更新後にも再検査する順番が重要だ。

FTPが使えない場合はどう調べればよいか

ホスティングのファイルマネージャーやSSHを使う。SSHが利用できるなら find コマンドで直近に変更された PHP を一覧化できる。レンタルサーバーによっては管理画面からファイルマネージャーが提供されるため、まずサーバー管理パネルを確認する。

パスワードを変更するだけでも大丈夫か

不十分だ。ファイルを削除して脆弱性を塞ぎ、バックドアを検索し、ログを確認するまでが一連の駆除になる。パスワード変更は侵入経路を断つ一部であり、単独では再侵入を防げない。

この記事のポイント

  • mu-pluginsフォルダは管理画面のプラグイン停止の対象外なので必ず手動で確認する
  • ppckやqtt-を含む不審なPHPファイルとテーマ内の偽.jsファイルを削除する
  • ドロッパーや検証用txtファイルも含めてバックドアを全検索する
  • Thrive ArchitectのCVE-2026-66694対策として最新版へ更新する
  • WordPress管理画面とFTPやデータベースの全パスワードを変更して二要素認証を有効化する