本文约 3,082 字,预计阅读 8 分钟。文中命令与配置均经过实测,可直接参考执行。
引言
网站某个按钮点了没反应、图片懒加载转圈、后台编辑器卡死,你按下 F12 打开控制台,看到一行红色报错:”Uncaught SyntaxError: Unexpected token ‘<‘”(新版 Chrome 还会补充说明:<!DOCTYPE ... is not valid JSON)。很多人第一反应是”我的 JS 写错了”,其实这是个典型的误解:这个报错几乎从来不是说你的脚本有语法错误,而是说代码期待收到 JSON 数据,结果服务器返回了一段 HTML。因为 HTML 通常以 < 开头(比如 <!DOCTYPE html>),JSON 解析器一看到 < 就抛错。所以真正要查的不是 JS 文件本身,而是”哪一个请求返回了 HTML、为什么”。本文就教你用浏览器自带的工具把”肇事请求”揪出来,再逐个修复。
故障原因分析
- 接口返回了 HTML 而不是 JSON。 前端
fetch或XMLHttpRequest去请求某个接口,服务器因为出错返回了一个错误页、登录页或 404 页面,前端却按 JSON 去解析,遇到<就报错。
- AJAX 请求被拦截或重定向。 WordPress 的
admin-ajax.php被安全插件、WAF 或登录校验挡住,返回的是拦截页或跳转到登录页,响应内容就是 HTML。
- PHP 有报错输出到响应里。 主题或插件的 PHP 在输出 JSON 前打印了警告、Notice,甚至文件开头带了 BOM 或空白字符,导致 JSON 前多出内容,前端解析失败。
- JS 或 CSS 资源 404。 某个脚本文件被删、路径写错、CDN 未同步,服务器对
.js请求返回了 404 的 HTML 页面,浏览器把这段 HTML 当脚本执行,直接报<语法错误。
- 接口地址被改成了内页链接。 比如 AJAX 地址写成了相对路径,在带子目录或伪静态异常的环境下被解析到某个页面,返回整页 HTML。
分步解决方案
第一步:用网络面板定位”肇事请求”
- 在报错页面按 F12 打开开发者工具,切到 Console(控制台) 面板,点开报错右侧的文件链接,通常能看到报错来自哪段代码。
- 切到 Network(网络) 面板,勾选筛选里的 Fetch/XHR,然后重新触发一次出问题的操作(比如点那个按钮)。
- 逐个点开请求,看 Response(响应) 标签:
- 响应以
<开头、内容是 HTML → 就是它。 - 看它的 Status(状态码):403 多为被拦截,404 多为路径错误,200 但内容是 HTML 多为接口内部出错或被重定向。
- 看 Request URL,记下这个接口地址,这是你后面要重点排查的对象。
第二步:按状态码分情况处理
如果是 404: 说明请求的地址根本不存在。常见于固定链接失效或资源文件丢失。
- 进入后台 设置 → 固定链接,点一次”保存更改”刷新重写规则。
- 用 FTP 确认对应的
.js文件是否真的存在;不存在的,重新上传或用插件重新生成。 - 若用了 CDN,去 CDN 后台刷新缓存。
如果是 403 或被跳转: 多半是安全插件、WAF 或登录校验在拦截 admin-ajax.php。
- 临时停用安全类插件(Wordfence、Wordfence、iThemes Security 等),重试操作;恢复后把
admin-ajax.php或/wp-json/加入白名单。 - 宝塔面板可在 网站 → 防火墙 中放行
/wp-admin/admin-ajax.php。 - 如果请求被跳转到登录页,检查是否有插件开启了”强制登录才能访问”。
如果是 200 但内容是一段 HTML: 说明接口执行过程中 PHP 出错了。
- 打开
wp-content/debug.log看具体报错(先在wp-config.php开启日志):
// wp-config.php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
- 根据日志里的文件名和行号,定位是哪个插件或主题出错,升级或临时停用它。
第三步:检查 PHP 是否输出了多余内容
如果日志里看到类似 “headers already sent” 或 JSON 前有空白,检查出错文件的以下情况:
- 文件开头是否多出空格、空行或 BOM。用编辑器把文件以 UTF-8 无 BOM 格式重新保存。
<?php标签前是否有空行。- 是否有调试用的
echo、var_dump、print_r忘在代码里。
WordPress 的接口输出应该统一走 wp_send_json_success() / wp_send_json_error(),它们会自动设置 JSON 响应头。如果你在开发接口,别用手工 echo 拼 JSON:
// 正确的 AJAX 返回方式
function mimangfei_get_data() {
$data = array( 'status' => 'ok', 'list' => array( 1, 2, 3 ) );
wp_send_json_success( $data ); // 会自动输出 JSON 并结束请求
}
add_action( 'wp_ajax_mimangfei_get_data', 'mimangfei_get_data' );
add_action( 'wp_ajax_nopriv_mimangfei_get_data', 'mimangfei_get_data' );
第四步:清缓存并复测
修改后清除浏览器、CDN、页面缓存和 OPcache,再用无痕窗口复测,避免被旧缓存误导。
常见踩坑点
- 以为是自己写的 JS 有语法错误。 这个报错 99% 与 JS 语法无关,问题在服务器返回的内容上,别去反复检查 JS 文件。
- 只看 Console 不打开 Network。 Console 只告诉你”解析失败”,Network 才能告诉你”是哪个请求、返回了什么”。
- 把安全插件长期关停。 排查时临时停用可以,问题定位后要加白名单并重新启用。
- JSON 前后有多余字符。 调试输出、BOM、闭标签后的空格都会破坏 JSON,务必清理干净。
- 改完不清缓存。 CDN 与浏览器缓存会继续返回旧的错误响应,无痕 + 清缓存是验证的标配。
- 忽略插件间的冲突。 有时是 A 插件输出内容打断了 B 插件的接口,逐个停用插件排查最有效。
总结
Unexpected token '<' 的核心含义只有一句:本该收到 JSON,却收到了 HTML。它是一盏”指路明灯”,而不是难解的玄学错误。正确的排查路径是先打开浏览器网络面板,找出哪个请求返回了 HTML,再看它的状态码——404 查固定链接和资源路径,403 查安全拦截,200 却返回 HTML 就去看 debug.log 里的 PHP 报错。修好之后,别忘了清一次 CDN 和页面缓存再验证,并顺手把 WP_DEBUG 调到只记日志不显示。养成用 Network 面板定位请求的习惯,以后再遇到这类”页面功能失灵 + 控制台红色报错”的问题,你都能迅速锁定真凶。
