Contact Form 7の保存先プラグインを入れ替えるため、ローカル環境でテスト送信を試したところ、レスポンスがstatus: spamで弾かれて保存されませんでした。reCAPTCHAプラグインを無効化しても変わらず、原因はCF7本体に登録されたreCAPTCHA v3のサイトキーでした。切り分けの手順を書きます。
REST API経由のテスト送信がspamになる
CF7はREST APIのエンドポイントを持っているので、ブラウザを開かずにコマンドラインからテスト送信できます。
curl -s -X POST \
"http://localhost:8090/wp-json/contact-form-7/v1/contact-forms/9/feedback" \
-F "_wpcf7=9" -F "_wpcf7_version=5.2.2" -F "_wpcf7_locale=ja" \
-F "_wpcf7_unit_tag=wpcf7-f9-p2-o1" -F "_wpcf7_container_post=2" \
-F "your-name=テスト" -F "your-email=test@example.com" \
-F "your-message=テスト送信です"
URLの9はフォームの投稿IDです。wp post list --post_type=wpcf7_contact_formで確認できます。フィールド名はフォームの定義(wp post meta get 9 _form)に合わせます。
返ってきたのがこれです。
{
"into": "#wpcf7-f9-p2-o1",
"status": "spam",
"message": "メッセージの送信に失敗しました。間をおいてもう一度お試しいただくか、別の手段で管理者にお問い合わせ下さい。"
}
statusがspamです。バリデーションエラー(validation_failed)ではないので、フィールドの指定は通っています。スパム判定で止められているだけです。この状態では保存もメール送信も行われません。
reCAPTCHAプラグインを無効化しても変わらない
最初に疑ったのはreCAPTCHAです。ローカル環境ではreCAPTCHAのトークンを送れないので、当然弾かれます。プラグインを無効化して再送しました。
$ wp plugin deactivate wpcf7-recaptcha
Success: Deactivated 1 of 1 plugins.
結果は変わらずstatus: spam。ここで手を止めて、何が判定しているのかを直接見ることにしました。
wpcf7_spamに登録されたフックを列挙する
CF7のスパム判定はwpcf7_spamフィルタで行われます。ここに何が登録されているかを、優先度つきで出力させます。mu-pluginsに一時ファイルを置きました。
<?php
// 一時デバッグ: wpcf7_spamに登録された全フックと最終値を出す
add_filter('wpcf7_spam', function($spam) {
global $wp_filter;
$names = array();
if (isset($wp_filter['wpcf7_spam'])) {
foreach ($wp_filter['wpcf7_spam'] as $prio => $cbs) {
foreach ($cbs as $id => $cb) {
$f = $cb['function'];
if (is_string($f)) $n = $f;
elseif (is_array($f)) $n = (is_object($f[0]) ? get_class($f[0]) : $f[0]) . '::' . $f[1];
else $n = 'closure';
$names[] = $prio . ':' . $n;
}
}
}
error_log('[SPAMDEBUG] hooks=' . implode(' | ', $names));
error_log('[SPAMDEBUG] final_spam=' . var_export($spam, true));
return $spam;
}, 9999);
優先度9999で登録しているので、他のフックが処理を終えた後の最終値が見えます。再送してログを見ます。
[SPAMDEBUG] hooks=9:wpcf7_recaptcha_verify_response | 10:wpcf7_akismet | 9999:closure
[SPAMDEBUG] final_spam=true
wpcf7_recaptcha_verify_responseが優先度9で登録されたままでした。プラグインは無効化したのに残っている——つまりこれはプラグイン由来ではなく、CF7本体に内蔵されたreCAPTCHA連携です。CF7はバージョン5.1からreCAPTCHA v3を本体機能として持っています。
設定を確認すると、サイトキーが登録済みでした。
$ wp option get wpcf7 --format=json | python3 -c "
import json,sys
d = json.load(sys.stdin)
print('recaptchaキー設定:', 'recaptcha' in d)
print('sitekey登録あり:', bool(d.get('recaptcha')))
"
recaptchaキー設定: True
sitekey登録あり: True
本番からコピーしたDBを使っているローカル環境なら、この設定もそのまま入ってきます。プラグイン一覧を見るだけでは気づけない理由がこれでした。reCAPTCHA v3はトークンなしの送信を無条件でスパム扱いするので、curlからのテスト送信は必ず弾かれます。
検証を通す方法
本番の設定を消したくないので、フィルタ自体を一時的に無効化しました。同じくmu-pluginsに置くファイルです。
<?php
// 検証用の一時ファイル(検証後に必ず削除する)
// CF7のspam判定を無効化し、REST経由のテスト送信を通す
add_filter('wpcf7_spam', '__return_false', 99);
優先度99は、reCAPTCHA(9)とAkismet(10)より後に実行させるための指定です。これで再送すると通りました。
status: mail_failed
mail_failedはメール送信の失敗です。ローカル環境にメールサーバーがないので当然で、スパム判定は抜けています。目的は保存先の確認だったので、これで十分でした。実際に保存されているか見ます。
$ wp post list --post_type=flamingo_inbound --post_status=any --fields=ID,post_title
ID post_title
2898 [your-subject]
$ wp post meta get 2898 _field_your-name
テスト
$ wp post meta get 2898 _field_your-message
テスト送信です
メール送信に失敗した送信でも、保存側には記録が残っていました。フォームからの送信を取りこぼさない仕組みとして、この挙動は確認しておく価値があります。
検証用ファイルは必ず消す
mu-pluginsに置いたファイルは、管理画面のプラグイン一覧に出てこないので消し忘れやすい場所です。スパム判定を無効化したまま公開環境に持ち込むと、フォームがスパムで埋まります。
rm wp-content/mu-plugins/test-nospam.php
検証で作られたレコードと、無効化したプラグインも元に戻します。
wp post delete 2898 --force
wp plugin activate wpcf7-recaptcha
ローカル環境が本番のDBを引き継いでいる場合、プラグインを無効化しても本体側の設定は生き続けます。「プラグインを止めたのに挙動が変わらない」時は、その機能が本体に取り込まれていないかを確認すると早く解決します。フィルタに何が登録されているかを列挙するデバッグは、CF7以外でも同じやり方で使えます。