HTML转义是将可能破坏结构的字符(如

HTML转义是编码动作,特殊字符是内容现象
HTML转义不是“某种字符”,而是把可能破坏HTML结构的字符(比如 、<code>&、")替换成对应命名实体或十进制/十六进制字符引用的过程。特殊字符(如 ©、ñ、€)本身不破坏解析,但若未声明正确编码或未用UTF-8保存文件,就会显示为乱码或方块——这是编码声明和传输层的问题,和转义无关。
常见错误现象:< 在页面上显示成字面的 < 而不是 ,说明被双重转义;而 <code>café 显示成 café,基本是文件存成了 Latin-1 却声明了 UTF-8。
- 用户输入含
<script></script>时,必须转义才能安全插入 HTML;但输入日本語只需确保文档是 UTF-8 且<meta charset="utf-8">存在 -
是转义,是其等价的数字字符引用;而¥是普通 UTF-8 字符,无需转义(除非目标环境只支持 ASCII) - 服务端模板(如 Jinja2、EJS)默认自动转义变量插值,但
{{ user_input|safe }}或会跳过——这里容易漏掉 XSS 防护
哪些字符必须转义?看上下文,不是看“长得怪”
是否需要转义,取决于你把字符串插到 HTML 的哪个位置。同一段文本,在 innerHTML、href 属性、onclick 内联事件里,要处理的字符完全不同。
例如:" onclick="alert(1)" 插入属性值时,双引号必须转成 ";但插在文本节点中," 完全合法,转不转都行。
立即学习“前端免费学习笔记(深入)”;
- 在 HTML 文本内容中:只需转义
、<code>>、&("和'仅在属性值中必要) - 在
href或src中:重点防javascript:伪协议,需对用户输入做 URL 编码(encodeURIComponent()),而非 HTML 转义 - 在
onxxx事件属性中:最危险,应完全避免拼接 JS 代码;若必须,需同时 HTML 转义 + JS 字符串转义(如\x27代替单引号)
JavaScript 里用 textContent 还是 innerHTML?
这是最直接的“转义开关”。用 textContent 赋值,浏览器自动把所有内容当纯文本处理,<strong>hello</strong> 会原样显示,不会解析成加粗文字——等于免费做了 HTML 转义。
而 innerHTML 直接触发 HTML 解析,传入未转义的用户数据等于开后门。别信“我只替换几个字符”,手动白名单过滤极难覆盖所有绕过方式(比如 <img src="x" onerror="alert(1)" alt="HTML转义和特殊字符有区别吗_HTML转义与特殊字符兼容方案【收藏】" >)。
- 显示用户昵称、评论正文等纯内容 → 优先用
textContent或innerText - 必须渲染富文本(如后台配置的 banner 描述)→ 先用成熟库如
DOMPurify.sanitize(),而不是自己写正则 -
document.write()已废弃,它和innerHTML同样危险,且无法被 CSP 有效拦截
服务端输出前没转义,前端补救还来得及吗?
来得及,但代价高、易遗漏。比如 PHP 模板里写了 <div>= $user_input ?></div>,没加 htmlspecialchars(),前端拿到的已经是“已注入”的 HTML 字符串。此时再用 JS 做转义,只能修复后续动态插入的内容,对初始 DOM 无效。
更麻烦的是:如果服务端返回的是 JSON,而前端用 eval() 或 JSON.parse() 后直接塞进 innerHTML,那 JSON 里的字段值就又成了新入口。
- 前后端约定:API 返回的文本字段(
title、content)默认不带 HTML 标签,前端一律用textContent渲染 - 若 API 明确返回 HTML 片段(如 CMS 富文本字段),响应头应带
X-Content-Type-Options: nosniff,且前端必须用DOMPurify过滤后再写入 - Webpack/Vite 构建时,
html-loader或vite-plugin-html对模板字符串不做转义——这类构建时插值也要人工检查
真正容易被忽略的点:CSS 中的 content 属性、SVG 的 title 元素、甚至 alt 属性,都可能成为未过滤用户输入的盲区。它们不走 HTML 解析器主流程,但依然能触发执行上下文(比如 url(javascript:...) 在旧版 IE)。



















