根本原因是iOS Safari和Android WebView对accept-charset支持不一致且默认fallback编码依赖系统环境:iOS缺失meta时fallback至ISO-8859-1(%C4%E3),Android WebView可能继承GBK系统编码而忽略UTF-8声明,三者(meta、form、HTTP头)任一不统一即导致乱码。

移动端表单提交乱码,90% 是 HTML 页面编码、表单属性、服务端解码三者没对齐,不是某一方单独的问题。
为什么 iOS Safari 和 Android WebView 表单提交后中文变问号或乱码
根本原因在于:移动端浏览器对 accept-charset 的支持不一致,且默认 fallback 编码依赖系统环境而非页面声明。
- iOS Safari 在缺失
<meta charset="UTF-8">时,会 fallback 到ISO-8859-1,导致你被编码成%C4%E3(ISO-8859-1 编码),而非标准 UTF-8 的%E4%BD%A0 - 某些 Android WebView(尤其旧版 ROM)继承系统编码,比如国产厂商预装的 GBK 环境,
accept-charset="UTF-8"可能完全被忽略 -
accept-charset在 Chrome/Firefox 中有效,但在部分 WebView(如 Android 7.x)中不生效,甚至被静默丢弃
必须检查的三个硬性位置:meta、form、HTTP 响应头
这三个地方任意一个不一致,就足以让整个链路乱码。顺序不能错,优先级从高到低:
- HTML 文件开头前 1024 字节内,必须有且仅有一个
<meta charset="UTF-8">,且不能有任何 BOM、注释、空格或 JS/CSS 在它前面 -
<form>标签上显式写accept-charset="UTF-8"(注意大小写,只认UTF-8,utf8或UTF8无效) - 服务端返回该 HTML 时,HTTP 响应头必须含
Content-Type: text/html; charset=UTF-8;若 Nginx/Apache/Express 返回了charset=gbk,<meta>将被无视
验证方式:Chrome DevTools → Network → 刷新 HTML 请求 → Headers → Response Headers 查看 Content-Type;再用 head -c 3 index.html | xxd 检查是否含 BOM(ef bb bf)。
立即学习“前端免费学习笔记(深入)”;
POST 提交后服务端收不到 UTF-8 参数?重点查 request.setCharacterEncoding()
Java/Tomcat 场景下,即使前端全对,服务端仍可能拿到乱码,因为 Tomcat 默认用 ISO-8859-1 解码 POST body。
-
request.setCharacterEncoding("UTF-8")必须在调用request.getParameter()之前执行,且不能晚于任何getInputStream()或getReader()调用 - 推荐统一用 Filter 实现,避免遗漏:
if ("POST".equalsIgnoreCase(request.getMethod())) { request.setCharacterEncoding("UTF-8"); } - Spring Boot 用户请确认
CharacterEncodingFilter已启用,且配置项spring.http.encoding.force=true和spring.http.encoding.charset=UTF-8 - PHP 用户需在接收前手动转码:
$name = iconv("ISO-8859-1", "UTF-8", $_POST['name']);(仅当确定前端发来的是 ISO-8859-1 编码时)
用 fetch + FormData 提交文件时后端收不到 $_FILES?WebView boundary 兼容性坑
这不是编码问题,而是 multipart boundary 生成机制在低端 WebView 中失效导致的解析失败。
- Android WebView 7.x 及更早版本不支持
fetch()自动设置boundary,发出的请求头是Content-Type: multipart/form-data,但 body 缺少分隔符,PHP 的$_FILES为空,php://input也读不到完整数据 - Node.js 使用
multer时会报Unexpected end of multipart data,日志里看不到合法的------WebKitFormBoundary...开头 - 修复方式:改用
XMLHttpRequest显式构造 FormData 并设置Content-Type头;或降级为传统<form enctype="multipart/form-data">提交 - 兼容底线:Chrome 57+、iOS 12.2+ 才真正支持
fetch+FormData自动 boundary,低于此版本必须绕过
最易被忽略的一点:即使所有编码设置都正确,只要 HTML 文件本身保存为 GBK 或带 BOM 的 UTF-8,<meta charset="UTF-8"> 就已失效——浏览器根本没机会读到它。



















