必须将置于最开头,因浏览器解析HTML时会提前扫描前1024字节寻找编码声明,若未及时读取该标签,便按默认编码(如ISO-8859-1)解码后续中文、emoji等内容,导致标题乱码、DOM异常、脚本执行失败;同时文件本身必须为UTF-8无BOM编码,否则BOM可能干扰DOCTYPE识别或被误判,且HTTP响应头中charset若与meta冲突,将优先采用响应头,故三者须严格对齐。

必须在 最开头写 </meta charset="UTF-8">,且文件本身也得是 UTF-8 编码,缺一不可。
为什么 <meta charset> 必须放在 最前面
浏览器解析 HTML 时,一旦遇到非 ASCII 字符(比如中文、emoji),会立即按当前已知编码解码;如果此时还没读到 <meta charset>,就只能退回到默认编码(通常是 ISO-8859-1 或系统 locale),导致标题、脚本、样式里的中文直接乱码。
常见错误现象:
-
<meta charset="UTF-8">写在<title></title>后面 → 页面标题显示为 “” - 前面有空格、注释或 BOM 头 → 浏览器完全忽略该
<meta>标签 - 用
<meta http-equiv="Content-Type">替代 → 在部分旧版 Safari 和微信内置浏览器中失效
如何确认 HTML 文件本身是 UTF-8 编码
编辑器里看到“UTF-8”不等于文件真是 UTF-8。BOM(Byte Order Mark)尤其危险:VS Code 默认保存为 “UTF-8 with BOM”,而 BOM 会卡在 前面,让浏览器误判文档类型。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- VS Code:右下角点击编码名 → 选 “Save with Encoding” → “UTF-8”(不是 “UTF-8 with BOM”)
- Sublime Text:File → Reopen with Encoding → UTF-8;再 File → Save with Encoding → UTF-8
- 用命令行检查:
file -i your-page.html应返回charset=utf-8,不是charset=iso-8859-1或charset=us-ascii - 用十六进制查看器确认开头三个字节不是
EF BB BF(即 BOM)
HTTP 响应头中的 charset 优先级高于 <meta>
如果服务器返回的 HTTP 响应头里有 Content-Type: text/html; charset=GBK,哪怕你写了 <meta charset="UTF-8">,浏览器也会优先用 GBK 解码 —— 这是规范强制行为。
排查方式:
- 浏览器开发者工具 → Network → 点开 HTML 请求 → Headers → Response Headers → 查看
content-type - Nginx 默认不带 charset,需显式配置:
charset utf-8;(注意不是default_type) - Apache 可在
.htaccess加:AddDefaultCharset UTF-8 - PHP 输出前不要调用
header("Content-Type: text/html; charset=GBK")
动态模板里容易漏掉的转码环节
EJS、Twig、PHP 输出变量时,如果原始数据来自数据库或用户输入,且未统一转成 UTF-8,<meta charset="UTF-8"> 也救不了 —— 浏览器拿到的是已经错乱的字节流。
关键点:
- MySQL 连接层要设
SET NAMES utf8mb4,不只是建表用utf8mb4_unicode_ci - PHP 中用
mb_convert_encoding($str, 'UTF-8', 'GBK')转旧数据,别依赖iconv的模糊容错 - Node.js 的
fs.readFile必须显式传'utf8'编码参数,否则返回 Buffer 容易被 toString() 错解 - CDN 缓存了旧版 GBK 编码的 HTML?清缓存后还要确认源站响应头和文件编码同步更新
真正出问题的地方,往往不是没写 <meta charset>,而是文件编码、HTTP 头、数据源三者中至少有一处没对齐。每次改完记得查 Network 面板里的响应头和响应体原始字节,别只信编辑器右下角显示的“UTF-8”。



















