Google reCAPTCHA v2本地运行正常 部署线上网站始终验证失败
ASP.NET MVC 线上部署Google reCAPTCHA v2验证失败排查方案
问题背景
- 基于ASP.NET MVC搭建的网站本地集成Google reCAPTCHA v2(复选框版本),本地localhost环境功能运行完全正常
- 项目迁移部署到线上后,验证码可正常渲染、支持复选框点击交互,但服务端验证环节始终失败
- 已确认线上部署代码与本地完全一致,且已替换为对应环境的site key和secret key
现有实现代码
视图(View)代码
<div class="g-recaptcha" data-sitekey="my_site_key"></div> <div class="submit-frm-btn"> <button class="contact-btn bg-mediumBlue" type="submit"> Submit </button> </div>
控制器(Controller)代码
public ActionResult SaveForm(ContactUs contactUsForm) { // 获取验证码响应参数 var response = Request["g-recaptcha-response"]; string secretkey = "my_secret_key"; var client = new WebClient(); var result = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret=" + secretkey + "&response=" + response +"")); var obj = JObject.Parse(result); var status = (bool)obj.SelectToken("success"); ViewData["Message"] = status ? "Google reCaptcha validation success." : "Google reCaptcha validation failed."; // 验证通过后执行业务逻辑 if (status) { if (ModelState.IsValid) { // 表单入库逻辑 } } }
排查思路与解决方案
按出现概率从高到低排序:
- 检查reCAPTCHA后台域名配置
本地localhost属于reCAPTCHA默认放行的测试域名,无需手动配置即可使用。线上环境必须登录reCAPTCHA管理后台,找到对应生产环境密钥的配置页,将线上域名(不带协议头、不带路径,例如example.com、www.example.com)添加到「允许的域名」列表,保存后等待3-5分钟生效即可。这是该问题最高发的原因。 - 确认线上服务器可正常访问reCAPTCHA验证接口
现有代码中服务端验证是由你的线上服务器主动请求https://www.google.com/recaptcha/api/siteverify接口完成的,和用户端能否正常加载验证码脚本无关。如果你的线上服务器存在网络出口限制、无法连通Google服务,会直接导致请求失败、拿不到正确的验证结果。
排查方法:直接登录线上服务器,测试能否正常访问上述siteverify接口;同时给现有验证逻辑加异常捕获和日志,记录接口返回的完整内容(尤其是error-codes字段),根据返回的错误码可以直接定位具体原因。 - 修复参数拼接的编码问题
现有代码直接将g-recaptcha-response参数值拼接在URL中,未做URL编码。该参数值是长度较长、包含特殊字符的加密字符串,线上环境如果存在URL重写、WAF拦截、编码规则不一致的情况,很容易出现参数被截断、转义错误的问题,导致Google接口收到无效的响应值,直接返回验证失败。
建议将验证请求改为官方推荐的POST方式提交,同时对参数做标准编码,避免拼接带来的异常。 - 确认线上实际加载的密钥配置正确
虽然已手动替换密钥,但需要排查发布过程中是否存在配置文件被覆盖、配置转换规则错误、密钥带多余空格/换行的问题。可以临时在服务端验证逻辑前加日志,输出当前运行环境实际读取到的site key和secret key,确认和reCAPTCHA后台生成的生产环境密钥完全配对,没有混用测试环境密钥。 - 排查中间件拦截问题
如果线上服务器部署了WAF、反向代理、CDN服务,检查是否存在拦截g-recaptcha-response表单参数、篡改出站请求到siteverify接口的规则,这类拦截也会导致参数传递异常、验证失败。
代码优化参考
将原有GET拼接请求改为POST提交,增加异常捕获和错误日志,参考代码如下:
public ActionResult SaveForm(ContactUs contactUsForm) { var captchaResponse = Request["g-recaptcha-response"]; var secretKey = "你的生产环境secret key"; // 建议从配置文件读取,不要硬编码 var isSuccess = false; var errorMsg = string.Empty; try { if (string.IsNullOrEmpty(captchaResponse)) { errorMsg = "未获取到验证码响应"; } else { using (var client = new WebClient()) { // 官方推荐POST方式提交验证,避免GET参数编码问题 var pars = new NameValueCollection { { "secret", secretKey }, { "response", captchaResponse }, { "remoteip", Request.UserHostAddress } // 可选,传用户IP提升验证准确率 }; var responseBytes = client.UploadValues("https://www.google.com/recaptcha/api/siteverify", "POST", pars); var result = System.Text.Encoding.UTF8.GetString(responseBytes); var obj = JObject.Parse(result); isSuccess = (bool)obj.SelectToken("success"); if (!isSuccess) { // 记录错误码,方便排查 var errorCodes = string.Join(",", obj.SelectToken("error-codes")?.ToObject<string[]>() ?? new string[0]); errorMsg = $"验证码验证失败,错误码:{errorCodes}"; } } } } catch (Exception ex) { errorMsg = $"请求验证码验证接口异常:{ex.Message}"; // 这里记录服务端日志 } ViewData["Message"] = isSuccess ? "Google reCaptcha validation success." : $"Google reCaptcha validation failed. {errorMsg}"; if (isSuccess && ModelState.IsValid) { // 表单入库逻辑 } return View(contactUsForm); }
常见error-codes说明:
missing-input-secret:secret参数缺失invalid-input-secret:secret key无效missing-input-response:验证码响应参数缺失invalid-input-response:验证码响应参数无效bad-request:请求格式错误timeout-or-duplicate:验证码已过期或已被使用
内容的提问来源于stack exchange,提问作者Kyle
相关产品推荐
相关产品推荐

