WordPress AJAX请求返回验证页面而非预期JSON响应问题
场景与代码
在WordPress中运行AJAX调用,后端与前端代码如下:
后端PHP代码
add_action('wp_ajax_my_plugin_check_r', 'my_plugin_check_r'); function my_plugin_check_r() { if(isset($_POST['token'])){ $token = trim($_POST['token']); $response = ... do something echo json_encode($response); wp_die(); }else{ echo json_encode(false); wp_die(); } }
前端JS代码
var postData = [ {name: 'action', value: 'my_plugin_check_r'}, {name: 'token', value: v}, ]; $.ajax({ url: ajax_url, type: 'post', data: postData, dataType: 'json', }).done(function(r){ var response = JSON.parse(r) }).fail(function(jqXHR, textStatus, errorThrown) { });
异常现象
请求返回的并非代码生成的JSON响应,而是一段包含“Please wait while your request is being verified...”的HTML验证页面,内容如下:
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="robots" content="noindex, nofollow"> <title>One moment, please...</title> <style> body { background: #F6F7F8; color: #303131; font-family: sans-serif; margin-top: 45vh; text-align: center; } </style> </head> <body> <h1>Please wait while your request is being verified...</h1> <form id="wsidchk-form" style="display:none;" action="/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" method="get"> <input type="hidden" id="wsidchk" name="wsidchk"/> </form> <script> (function(){ var west=+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![])), east=+((+!+[]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])), x=function(){try{return !!window.addEventListener;}catch(e){return !!0;} }, y=function(y,z){x() ? document.addEventListener("DOMContentLoaded",y,z) : document.attachEvent("onreadystatechange",y);}; y(function(){ document.getElementById('wsidchk').value = west + east; document.getElementById('wsidchk-form').submit(); }, false); })(); </script> </body> </html>
网络面板显示状态码为200 OK,响应头信息:
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://www..... Age: 0 Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private Content-Encoding: gzip Content-Length: 657 Content-Type: text/html; charset=UTF-8 Date: Mon, 08 Jan 2024 18:40:59 GMT Expires: Wed, 11 Jan 1984 05:00:00 GMT Pragma: no-cache Referrer-Policy: strict-origin-when-cross-origin Server: Apache Vary: Accept-Encoding Via: 1.1 webcache1 (Varnish/trunk) X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-Powered-By: PHP/8.1.27 X-Robots-Tag: noindex X-Varnish: 1639091928
提问
该异常的原因是什么?该如何解决?
原因分析
从响应头的Via: 1.1 webcache1 (Varnish/trunk)和返回的验证页面来看,这是Varnish缓存或服务器层的人机验证机制触发了拦截。这类机制通常会针对非浏览器发起的请求、缺少必要Cookie/请求头的AJAX请求进行验证,防止自动化脚本或爬虫访问。
另外,前端代码存在冗余问题:dataType: 'json'已经告知jQuery自动解析JSON响应,但done回调里又调用JSON.parse(r),这会导致报错,但并非返回验证页面的直接原因。
解决方案
1. 确保AJAX请求携带必要验证信息
WordPress默认AJAX请求需要携带会话Cookie,同时要保证ajax_url是通过wp_localize_script传递的admin-ajax.php地址。修改前端请求,补充必要的请求头和Cookie配置:
var postData = { action: 'my_plugin_check_r', token: v }; $.ajax({ url: ajax_url, type: 'post', data: postData, dataType: 'json', xhrFields: { withCredentials: true }, headers: { 'X-Requested-With': 'XMLHttpRequest' } }).done(function(r){ // 无需再次解析,r已是处理好的JSON对象 console.log(r); }).fail(function(jqXHR, textStatus, errorThrown) { console.error(errorThrown); });
2. 绕过Varnish对AJAX请求的验证
如果服务器使用Varnish,可在VCL配置中添加规则,跳过对admin-ajax.php的缓存验证:
sub vcl_recv { if (req.url ~ "^/wp-admin/admin-ajax.php$") { return (pass); // 直接透传至后端,不缓存 } }
3. 检查WordPress安全插件设置
Wordfence、iThemes Security等安全插件会对AJAX请求做额外验证,需将你的AJAX动作my_plugin_check_r加入插件的白名单,避免被拦截。
4. 修复前端JSON解析冗余问题
删除done回调中的JSON.parse(r),避免重复解析导致的语法错误。
内容的提问来源于stack exchange,提问作者Toniq

