如何正确在PHP联系表单中集成reCaptcha V3?求助排查
问题排查与修复方案
我来帮你一步步拆解并解决遇到的这几个核心问题:
1. 解决ReferenceError: $ is not defined错误
这个报错的直接原因是页面没有引入jQuery库,但你的reCaptcha脚本里使用了$(jQuery的简写语法)。
修复步骤:
在页面的<head>标签中,务必在reCaptcha脚本之前引入jQuery库:
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
你也可以下载jQuery文件到本地服务器,使用本地路径引入,避免依赖外部CDN。
同时,你的reCaptcha脚本还有两个逻辑错误:
- 生成token隐藏字段时存在语法漏洞:
value="' + token + '>'缺少闭合的双引号,应改为value="' + token + '">' - 当前脚本会在页面加载完成后自动提交表单,这不符合用户手动提交的预期,应该改为监听表单的提交事件,在用户点击按钮时再获取token。
修改后的reCaptcha脚本:
<script> $(document).ready(function() { // 监听表单提交事件 $('#contact').on('submit', function(e) { e.preventDefault(); // 阻止默认提交行为 const form = this; grecaptcha.ready(function() { grecaptcha.execute('YOUR_SITE_KEY', {action: 'contact'}).then(function(token) { // 移除已存在的token和action字段(避免重复添加) $(form).find('input[name="token"], input[name="action"]').remove(); // 添加合法的隐藏字段 $(form).prepend('<input type="hidden" name="token" value="' + token + '">'); $(form).prepend('<input type="hidden" name="action" value="contact">'); // 提交表单 form.submit(); }); }); }); }); </script>
2. 解决"阈值设为1.0时表单仍可提交"的问题
这个问题的核心是原后端代码中,reCaptcha验证失败时没有设置$formok = false,所以即使reCaptcha判定为机器人,只要其他表单字段验证通过,表单依然会被提交。
另外还有两个细节需要修正:
$arrResponse["success"]是布尔值,不是字符串,应该用=== true判断,而非== '1'- 确保你确实修改了后端的阈值参数(比如从0.5改为1.0)
修复后的reCaptcha验证代码:
// reCaptcha V3 验证逻辑 define("RECAPTCHA_V3_SECRET_KEY", 'YOUR_SECRET_KEY'); $token = $_POST['token'] ?? ''; // 使用null合并运算符避免未定义索引报错 $action = $_POST['action'] ?? ''; // 先检查必要参数是否存在 if(empty($token) || empty($action)) { $formok = false; $errors[] = "reCaptcha验证失败:缺少必要参数"; } else { // 调用Google验证接口 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,"https://www.google.com/recaptcha/api/siteverify"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( 'secret' => RECAPTCHA_V3_SECRET_KEY, 'response' => $token, 'remoteip' => $_SERVER['REMOTE_ADDR'] // 可选:添加用户IP提高验证准确性 ))); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $arrResponse = json_decode($response, true); // 验证响应:注意success是布尔值,不是字符串 if($arrResponse["success"] === true && $arrResponse["action"] === $action && $arrResponse["score"] >= 1.0) { // 验证通过,继续执行后续逻辑 } else { $formok = false; $errors[] = "reCaptcha验证失败:你可能是机器人"; // 可选:添加调试信息(上线前移除) // var_dump($arrResponse); } }
3. 解决"添加$formok = false后表单一直提交失败"的问题
这个问题大概率是因为token没有正确提交到后端,导致reCaptcha验证一直失败,$formok被持续设为false。
你可以通过以下方式排查:
- 前端调试:提交表单前,打开浏览器开发者工具的Elements面板,查看
<form id="contact">内部是否生成了token和action隐藏字段。 - 后端调试:在reCaptcha验证代码前添加调试代码,检查是否收到了token:
// 调试用(上线前务必移除) var_dump($_POST); exit;
另外,后端代码还有一个致命问题:session_start()的位置错误,它必须放在文件的最开头(任何输出之前),否则session无法正确初始化,导致错误信息无法回显到表单页面。
修复后的后端代码开头:
<?php session_start(); // 必须放在最开头,确保session正常初始化 if( isset($_POST) ){ // 剩余代码... }
其他需要修复的小问题
- 后端
mail()函数参数为空:mail("",'',$emailbody,$headers);需要填写收件人邮箱、邮件主题等必要参数,例如:
$to = "your-email@example.com"; $subject = "新的联系表单提交"; mail($to, $subject, $emailbody, $headers);
- 表单中部分
<option>的value与文本不一致:比如<option value="sessions" ...>Events</option>,会导致后端收到的enquiry值是sessions而非Events,建议保持value与文本一致,或者后端处理时做映射修正。
内容的提问来源于stack exchange,提问作者ThunderMunch
相关产品推荐
相关产品推荐

