根本原因是浏览器未正确识别HTML文件的字符编码,导致UTF-8中文被误解析为ISO-8859-1等单字节编码,表现为问号、方块或控制台字体解码失败;关键在于<meta charset="utf-8">须位于<head>最前、文件保存编码与声明一致、且HTTP响应头Content-Type中的charset优先级更高,三者缺一不可。

为什么中文显示成问号或方块?
根本原因是浏览器没正确识别 HTML 文件的字符编码,导致 UTF-8 编码的中文被当成了 ISO-8859-1 或其他单字节编码来解析。最直接的表现是:、???、乱码方块,或控制台报 Failed to decode downloaded font 这类提示(尤其在引入中文字体时)。
<meta charset> 必须放在 <head> 最前面
这个标签不是可有可无的“建议”,而是浏览器解析 HTML 时决定初始编码的关键开关。如果它出现在 <title> 后面、或者被注释掉、或者写成了 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">(旧写法,兼容性差),都可能失效。
正确写法只有一种(且必须独占一行、无空格干扰):
<meta charset="utf-8">
注意以下几点:
立即学习“前端免费学习笔记(深入)”;
- 必须位于
<head>内,且是<head>中第一个非注释节点(推荐紧贴<head>开始标签) - 不要写成
UTF-8大写或utf8(少横杠),浏览器对大小写不敏感但规范要求小写加横杠 - 不要和
<title>调换顺序——哪怕只差一行,某些旧版 IE 和部分嵌入式 WebView 就会忽略它
文件保存编码必须与 charset 声明严格一致
声明了 utf-8,但用记事本另存为时选了 ANSI(Windows 下通常是 GBK),那所有声明都是白搭。常见错误场景:
- VS Code 默认保存为 UTF-8,但右下角编码显示为
GBK或ISO-8859-1→ 点击切换并「Save with Encoding」 - Sublime Text 保存时未勾选「Convert Line Endings to Unix」不影响编码,但若误选「UTF-8 with BOM」,部分老服务器(如 IIS 6)会把 BOM 当作响应头前的非法字符,导致
Cannot set headers after they are sent - Linux/macOS 终端用
iconv转码后未覆盖原文件,仍用旧文件部署 → 检查file -i index.html输出是否含charset=utf-8
HTTP 响应头 Content-Type 优先级高于 <meta>
当 Web 服务器(如 Nginx、Apache)返回的响应头里带了 Content-Type: text/html; charset=gbk,浏览器会无视 HTML 里的 <meta charset="utf-8">。这是线上环境最隐蔽的乱码原因。
验证方式很简单:
- Chrome DevTools → Network → 刷页面 → 点开
index.html→ 查看 Response Headers → 找content-type - Nginx 配置中检查是否有
charset gbk;或遗漏charset utf-8; - Node.js 的 Express 若用
res.send(),默认设Content-Type: text/html; charset=utf-8;但若手动res.set('Content-Type', 'text/html')却没带 charset,则退回到 meta 标签,风险上升
真正可靠的组合是:文件存为 UTF-8(无 BOM)、HTML 顶部有 <meta charset="utf-8">、服务器响应头明确声明 charset=utf-8 —— 三者缺一不可。漏掉任意一个环节,中文就可能在某个用户环境里突然变方块。



















