1.

reCAPTCHA「invalid-input-response」原因と対処|siteverify エラー解決

編集
この記事の要点
  • reCAPTCHA の invalid-input-response エラー
  • 原因: siteverify API に渡した response パラメータが不正(空・期限切れ・改ざん)
  • 対処1: フロントから g-recaptcha-response を正しく渡しているか確認
  • 対処2: ユーザがチャレンジ未完了 / トークン期限切れ(reCAPTCHA トークンは 2 分間有効
  • 対処3: トークンを使い回していないか(1 回限り有効

エラー内容

reCAPTCHA のサーバ側検証で以下のレスポンスが返る:

array(2) {
    ["success"] => bool(false)
    ["error-codes"] => array(1) {
        [0] => string(22) "invalid-input-response"
    }
}

または JSON で:

{
    "success": false,
    "error-codes": ["invalid-input-response"]
}

原因

https://www.google.com/recaptcha/api/siteverify に POST した response パラメータ(フロントエンドの reCAPTCHA トークン)が不正と判定されています。具体的には:

  • トークンが空: フロント側でユーザがチャレンジ未完了
  • トークンの期限切れ: reCAPTCHA トークンは生成後 2 分間のみ有効
  • トークン使い回し: 同じトークンを 2 回以上使うと無効
  • トークンが改ざん: フォーム送信前に JS で書き換えられた等
  • siteverify への送信形式が違う: GET / 引数間違い

対処1: フロント側の確認

HTML フォームに reCAPTCHA ウィジェットを正しく設置:




    


    

送信時に g-recaptcha-response という name の hidden input が自動生成されます。サーバ側ではこれを $_POST['g-recaptcha-response'] で受け取ります。

対処2: サーバ側で siteverify を正しく呼ぶ

PHP

function verifyRecaptcha($token, $secretKey) {
    $url = 'https://www.google.com/recaptcha/api/siteverify';

    $params = [
        'secret'   => $secretKey,
        'response' => $token,
        'remoteip' => $_SERVER['REMOTE_ADDR'],  // 任意
    ];

    $options = [
        'http' => [
            'method'  => 'POST',
            'header'  => 'Content-type: application/x-www-form-urlencoded',
            'content' => http_build_query($params),
        ],
    ];
    $context  = stream_context_create($options);
    $response = file_get_contents($url, false, $context);
    $result = json_decode($response, true);

    if (!$result['success']) {
        // エラーコード配列を返す
        return ['ok' => false, 'errors' => $result['error-codes'] ?? []];
    }

    // v3 ならスコアもチェック
    if (isset($result['score']) && $result['score'] < 0.5) {
        return ['ok' => false, 'errors' => ['low-score'], 'score' => $result['score']];
    }

    return ['ok' => true, 'result' => $result];
}

// 使い方
$token = $_POST['g-recaptcha-response'] ?? '';
if (empty($token)) {
    die('reCAPTCHA を完了してください');
}

$result = verifyRecaptcha($token, getenv('RECAPTCHA_SECRET'));
if (!$result['ok']) {
    error_log('reCAPTCHA failed: ' . json_encode($result['errors']));
    die('検証に失敗しました');
}
// 検証 OK → フォーム処理続行

Laravel

// Laravel 標準の Validator にカスタムルール
// または anhskohbo/no-captcha パッケージ

// composer require anhskohbo/no-captcha

// config/services.php
'recaptcha' => [
    'key' => env('RECAPTCHA_SITE_KEY'),
    'secret' => env('RECAPTCHA_SECRET_KEY'),
],

// Controller
public function submit(Request $request) {
    $request->validate([
        'g-recaptcha-response' => 'required|captcha',
        'email' => 'required|email',
    ]);
    // ... 処理
}

対処3: reCAPTCHA v3 のスコアも検証

v3 はチャレンジなしでバックグラウンド判定。score(0.0〜1.0)でボット判定:

// フロント (v3)
grecaptcha.ready(function () {
    grecaptcha.execute('YOUR_SITE_KEY', {action: 'submit'})
        .then(function (token) {
            document.getElementById('recaptcha-token').value = token;
            document.getElementById('myForm').submit();
        });
});
// サーバ側 (v3) は v2 と同じ siteverify を叩く
$result = verifyRecaptcha($token, $secret);

// v3 だけ追加で score を見る
if ($result['ok'] && $result['result']['score'] < 0.5) {
    // 怪しい
    error_log('Low score: ' . $result['result']['score']);
}

// 'action' も一致確認
if ($result['result']['action'] !== 'submit') {
    error_log('Unexpected action');
}

エラーコード一覧

error-code意味対処
missing-input-secretsecret パラメータ未送信secret を必ず渡す
invalid-input-secretsecret が間違っているGoogle Console で正しい secret を確認
missing-input-responseresponse パラメータ未送信g-recaptcha-response を必ず渡す
invalid-input-responseresponse が無効(空・期限切れ・改ざん)本記事
bad-requestリクエスト形式が不正POST / URL エンコード形式を確認
timeout-or-duplicateトークン期限切れ or 使い回し毎回新規トークン取得

SPA / Ajax での注意

  • トークンを保存して再利用しない — 必ず送信直前に grecaptcha.execute で新規生成
  • 2 分のタイマー: フォーム入力中に時間が経つとトークン期限切れ → 送信直前に再取得
  • action 名は固定的に — サーバ側の検証と一致させる
  • Ajax 送信時のヘッダ — CSRF と reCAPTCHA は別物。両方必要

セキュリティ補足

  • secret key は絶対にフロントに置かない — サーバ側のみで保持
  • siteverify は HTTPS で — secret が漏洩しないよう
  • レート制限: スコアが低いリクエストの連発は IP ブロック等で対応
  • v3 のスコア閾値: 0.5 が一般的だが、サイトの実情で調整
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. invalid-input-response
  2. ERROR for site owner: Invalid site key