A reCAPTCHA bad-request error is not a verdict about the user. It means the verification request that reached Google was invalid or malformed. That distinction matters: changing score thresholds, retrying every failed token, or blaming the browser will not fix a backend request that never arrived in the shape the API expects.
What bad-request means
Google's verification endpoint is the siteverify API URL. The request method is POST, the required parameters are secret and response, and remoteip is optional. In the same error-code table, Google defines bad-request as an invalid or malformed request.
That usually points to transport and serialization. The backend might send JSON when the library expects form fields, put the token under captchaToken while Google expects response, accidentally pass an empty body, or run middleware that consumes the request before the verifier reads it. The user can solve the challenge correctly and still hit bad-request if the server submits the wrong envelope.
Start with the exact outbound request
Log the request shape without logging secret values or full user tokens. You need method, content type, destination host, parameter names present, token length bucket, route, framework, and the provider response code. Redact the secret completely and truncate the response token to a short prefix or hash if you need correlation.
A good debug line says: POST to siteverify, content-type application/x-www-form-urlencoded, secret present, response present, remoteip omitted, response status 200, provider error bad-request. A bad debug line prints the entire secret and token into an application log that every support tool can see.
Framework traps that create malformed requests
In PHP, Laravel, Rails, Node, Go, and .NET, the bug is often not the HTTP client itself but the convenience wrapper around it. Some clients default to JSON bodies, some require a form_params or FormUrlEncodedContent option, and some silently drop null values. A missing response field can be created by a naming mismatch between frontend form code and backend validation code.
Check the path from browser to backend before checking Google. Is the frontend sending g-recaptcha-response, recaptcha_token, or token? Is server validation renaming it? Does a proxy strip large form bodies? Does an AJAX submit send FormData in one browser path and JSON in another? The siteverify call should receive one fresh token for one protected action.
Do not fix malformed requests by weakening verification
If siteverify returns bad-request, do not treat it as a low-risk pass and do not fall back to accepting the form. Fail closed with a user-safe retry path, preserve the form data, and alert the integration owner. A malformed request means your security check did not run.
For recurring failures, add a small health test that posts a placeholder-safe request in staging and asserts that your HTTP client is sending the expected method, host, encoding, and parameter names. The test should never use production secrets in captured fixtures.
A practical bad-request checklist
Confirm POST, not GET. Confirm form encoding, not accidental JSON. Confirm secret and response are present and non-empty. Confirm the response token is the current token from the submitted form, not a token cached at page load. Confirm server code verifies the token once. Confirm logs redact secrets, tokens, and user identifiers.
Once the request shape is correct, remaining failures usually move into other error codes: missing-input-response, invalid-input-response, timeout-or-duplicate, or hostname mismatches. Those are different problems. The value of bad-request is that it narrows the first repair to transport, parameter names, and request construction.
Sources and further reading
Google reCAPTCHA verification docs for siteverify method, parameters, token restrictions, and error codes.
reCAPTCHA in React and single-page apps for token freshness patterns that prevent stale response values.
Invalid input response debugging for the next layer after transport is correct.
Try rCAPTCHA on your own site
Start with a minimal free testing plan, add a real site key, and see per-site verification data before moving to a paid tier.