recaptcha v2 前端加载失败或不显示,主因是 script 标签未正确加载(如 csp 拦截、403/404)或 data-sitekey 未写在空的 上;须确保脚本置于 body 内、密钥匹配 v2 类型且域名已白名单授权。

reCAPTCHA v2 前端加载失败或不显示?检查 script 标签和 sitekey
常见现象是页面空白、控制台报 grecaptcha is not defined,或复选框一直转圈。根本原因通常是脚本没加载成功,或 data-sitekey 写错位置。
-
<script src="https://www.google.com/recaptcha/api.js" async defer></script>必须在内(不能只放),且不能被 CSP 策略拦截(检查浏览器控制台 Network 标签页是否 403/404) -
data-sitekey必须写在<div class="g-recaptcha"> 上,不是 <code><form></form>或其他父容器;值必须是你在 reCAPTCHA 控制台 创建的 v2 密钥,不能混用 v3 的 sitekey - 如果用了框架(如 React/Vue),别在 JS 里动态插入
g-recaptchaDOM——reCAPTCHA 初始化依赖首次渲染时该元素已存在 - hCaptcha 默认把 token 放进一个
<textarea name="h-captcha-response" style="display:none"></textarea>,但这个 name 是固定值,**不能改**;如果你的后端期待的字段名是hcaptcha_token,就必须在 submit 事件里手动复制过去 - 示例 JS:在表单
submit回调中写document.getElementById("hcaptcha_response").value = document.querySelector("textarea[name='h-captcha-response']").value - 别用
display: none或visibility: hidden隐藏整个.h-captcha容器——这会导致 hCaptcha 不触发验证,token 永远为空 -
secret参数必须是后端私钥(secret key),**绝不能是前端用的 sitekey**;它必须通过环境变量或配置中心加载,严禁硬编码在代码或 HTML 注释中 -
response字段必须是前端拿到的原始 token 字符串,**不能做任何 trim()、base64 decode、JSON parse 等处理**——直接原样 POST 过去 - reCAPTCHA v3 的 token 有 2 分钟有效期,hCaptcha 的 token 约 120 秒;如果用户填表时间过长,提交时 token 已失效,结果就是 silent fail
- 测试时用 curl 手动发请求比靠前端调试更可靠:
curl -d "secret=xxx&response=yyy" https://hcaptcha.com/siteverify - reCAPTCHA 控制台里,v2/v3 的 sitekey 绑定了「允许的网域」列表;如果上线域名是
app.example.com,但你只填了example.com,验证会静默拒绝 - hCaptcha 要求请求头中的
Referer必须匹配白名单;Nginx/Apache 反向代理若清除了Referer,或者前端用了referrerpolicy="no-referrer",后端验证就会失败 - CDN 或 WAF(如 Cloudflare)可能默认剥离或重写
Referer,需在规则中显式放行
hCaptcha 表单提交后 token 总是校验失败?重点查字段名和隐藏 input 同步逻辑
你看到 h-captcha-response 值为空、或后端收到的是空字符串,大概率不是 SDK 没加载,而是表单提交前没把 token 抓出来塞进正确字段。
后端验证返回 success=false 却没报具体错误?先确认 secret key 和 response 是否传对
Google reCAPTCHA 的 /siteverify 或 hCaptcha 的 /siteverify 接口返回 {"success": false} 时,几乎从不带明确原因。错误往往藏在参数传递链里。
为什么本地开发能过、上线就失败?Referer 和域名白名单是隐形开关
reCAPTCHA 和 hCaptcha 都会校验请求来源。你在 localhost 测试成功,不代表生产环境能过——它们根本不看你的代码,只认域名和 Referer 头。
name 多了个下划线、Referer 少了一个斜杠、secret 被当成字符串拼进了 URL 而不是 POST body。这些地方不会报红错,只会让验证安静地失败。











