input[type="url"]不可靠,仅校验基础结构(如协议头和点号),不验证协议真实性、域名可解析性或路径合法性;必须trim()后用new URL()校验,且后端须独立校验。

别指望 input[type="url"] 自己拦住乱输的网址——它连 http://a 都放行,更别说带空格或中文的链接。必须用 pattern 或 JavaScript 补位,且后端校验不可省略。
为什么 input[type="url"] 不能直接信
浏览器只检查是否有协议头(如 http://)和至少一个点号,不验证域名是否可解析、路径是否合法、协议是否真实存在。常见失效场景:
-
foo://bar、https:///example.com会被认为合法 -
www.example(缺协议)被拒,但http://localhost:3000却能过 - 用户粘贴
https://example.com/路径(含中文)或https://a b(含空格),表单照样提交 - Safari 和旧版 Android WebView 对该类型支持不一致,提示文案和触发时机都可能错位
用 pattern 写一个实用正则
比抄“匹配所有 URL”的超长正则更靠谱的是聚焦常见合法输入,避开回溯爆炸和过度宽松。推荐这个:
pattern="https?://[^\s/$.?#].[^\s]*"
说明:
立即学习“前端免费学习笔记(深入)”;
-
https?锁定http或https,不强行要求ftp或file -
[^\s/$.?#]确保域名开头不是空格、/、$、.、?、#,防http://.example.com -
[^\s]*允许路径、查询参数、片段标识符,但明确禁止空格 - 末尾不用加
$—— 浏览器自动给你包上^...$,加了反而报Invalid regular expression
注意:pattern 只对 type="text"、type="url" 等生效,对 type="number" 完全无效;且它和原生 type="url" 校验逻辑并存,可能冲突,建议统一用 type="text" + pattern。
用 URL 构造函数做 JS 校验
比正则更稳,因为它是真正尝试解析,不是字符串匹配。但有两个硬约束必须处理:
- 必须先调
.trim()去首尾空格,否则" https://a "会抛TypeError - 只接受绝对 URL,相对路径(如
/api/user、./index.html)会直接失败 - 必须显式捕获
TypeError,不能只靠try/catch吞掉错误,否则无法反馈
示例:
function isValidUrl(str) {
try {
new URL(str.trim());
return true;
} catch (e) {
return false;
}
}
它不校验 DNS 可达性或证书有效性,只是格式守门员——这点和正则一样,别高估它的能力。
容易被忽略的细节
前端校验再严密,也挡不住开发者工具里直接改 HTML 或禁用 JS。真正起作用的永远是后端:用服务端语言(如 Node.js 的 url.parse、Python 的 urllib.parse)重新解析并校验协议、主机、端口、路径。前后端规则最好一致,比如都拒绝 javascript:、data: 这类危险协议。另外,setCustomValidity() 设过错误后,必须传空字符串重置,否则字段永远卡在 invalid 状态。



















