必须将<meta charset="UTF-8">置于<head>最开头且文件为UTF-8无BOM,否则浏览器扫描前1024字节失败即回退系统默认编码(如Windows的GBK);同时HTTP响应头Content-Type的charset优先级更高,三者(标签位置、文件编码、响应头)须严格一致。

meta charset 必须写在 <head> 最开头,且文件本身必须是 UTF-8(无 BOM),否则浏览器根本不会按你写的解码——它会直接 fallback 到系统默认编码(Windows 下通常是 GBK),中文立刻变方块或问号。
为什么 meta charset="UTF-8" 放错位置就失效
浏览器只扫描 HTML 前 1024 字节来找 meta charset,一旦前面有:
- 任何空格、换行(哪怕
<head>后多一个回车) - HTML 注释
<!-- --> - BOM 字节(
ef bb bf) - 其他标签如
<title>或<script>
它就会跳过,转而用系统默认编码解析后续所有字节。结果就是:代码里明明写了 UTF-8,但页面照旧乱码。
正确顺序只有一种:<head><meta charset="UTF-8"><title> —— 中间不能插任何东西。
立即学习“前端免费学习笔记(深入)”;
文件实际编码和声明必须严格一致
编辑器显示“UTF-8”不等于文件真是 UTF-8(无 BOM)。VS Code 默认可能存成 UTF-8 with BOM,Notepad++ 点“转为 UTF-8”却悄悄加了 BOM,Sublime 保存时也可能带签名。
验证和修复方法:
- VS Code:右下角点击编码 → Save with Encoding → 选
UTF-8(不是UTF-8 with BOM) - Notepad++:菜单栏「编码」→「转为 UTF-8 编码」(不是「UTF-8-BOM」)
- 命令行检查:运行
head -c 3 yourfile.html | xxd,输出含ef bb bf就说明有 BOM,必须清除后重存
只要文件含 BOM,<meta charset="UTF-8"> 就永远在第 1 字节前被挡住,形同虚设。
HTTP 响应头 Content-Type 优先级高于 meta
如果服务器返回的响应头是 Content-Type: text/html; charset=gbk,或者压根没写 charset 子句,那浏览器会直接忽略 meta 标签,按响应头执行。
排查方式:
- Chrome DevTools → Network → 刷新 → 找 HTML 请求 → Headers → Response Headers → 查
Content-Type - 终端用
curl -I https://yoursite.com/index.html看响应头
常见修复配置:
- Nginx:在
server或location块中加charset utf-8;(注意小写utf-8,不是UTF-8) - Apache:
.htaccess中写AddDefaultCharset UTF-8 - Express:在
res.send()或res.sendFile()前加res.set('Content-Type', 'text/html; charset=utf-8')
别混用 meta http-equiv 和 meta charset
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 是 HTML4 写法,现代浏览器虽兼容,但它比 <meta charset="UTF-8"> 解析更慢、更不可靠,且容易和响应头冲突。
必须做到:
- 只保留一个
<meta charset="UTF-8"> - 删掉所有
http-equiv形式的重复声明 -
charset值必须是UTF-8(全大写 U/T/F/-/8,不能是utf8、utf-8、UTF8)
大小写和拼写错误会导致部分嵌入式 WebView(比如旧版微信内置浏览器、IoT 设备)完全无法识别,直接 fallback。
真正卡住人的从来不是“要不要写 meta charset”,而是它前面有没有看不见的空格、BOM、注释,以及服务器有没有偷偷覆盖它——三者任一出问题,UTF-8 就只是个摆设。



















