base href 必须是绝对URL或根相对路径(如/subpath/),否则被浏览器静默忽略,导致资源404;它仅影响HTML解析时的src/href,对JS/CSS运行时路径无效,且必须位于head内首个位置。

base href 必须是绝对 URL 或根相对路径,否则直接失效
浏览器只认 href 值以 https://、http://、// 或 / 开头的 <base>;其他写法(比如 assets/、../static/、%PUBLIC_URL%/)会被静默丢弃——控制台不报错,但所有 <img src="logo.png"> 都会退回到按当前 HTML 文件位置解析,上线后批量 404。
常见翻车点:
-
<base href="static/">:缺协议和域名,被忽略 -
<base href="../assets">:含..,被忽略 -
<base href="https://example.com">:结尾没斜杠,Firefox/Opera 会截断成https://example.comcss/,导致css/app.css请求变成https://example.comcss/app.css
稳妥写法:<base href="/subpath/">(根相对,结尾带斜杠)或 <base href="https://cdn.example.com/v2.3/">(完整绝对 URL)。
它只改 HTML 解析阶段的 src/href,不碰 JS/CSS 运行时路径
<base> 不是全局 URL 重写器,它只在浏览器解析 HTML 文档时起作用。这意味着:
立即学习“前端免费学习笔记(深入)”;
- ✅ 生效:
<img src="logo.png">、<script src="app.js">、<link href="style.css">、CSS 文件里的background: url(icon.svg) - ❌ 完全无效:
fetch('./api/users')、import('./utils.js')、new Worker('./worker.js')、CSS 中的@import "reset.css" - ⚠️ 半生效:
document.baseURI会返回<base href>的值,可用于构造 URL(如new URL('data.json', document.baseURI)),但window.location和document.URL完全不变
很多人加了 <base href="/admin/"> 后发现 API 调用全挂了,就是因为写了 fetch('api/list')——它被解析成 /admin/api/list,而实际接口在 /api/list。
放错位置或重复声明,等于没写
<base> 必须是 <head> 的**第一个子元素**,且整个文档只能有一个。否则:
- 放在
<body>里 → 浏览器完全忽略,无提示 - 模板拼接时 SSR 多次注入(header 模板 + 页面模板各塞一个)→ 控制台提示
Multiple base elements detected. Only the first one is used.,后续被丢弃 - 用 JS 动态插入(如
document.head.appendChild(baseEl))→ 已解析的src/href不会重算,只影响之后新创建的元素
最稳做法:手写 HTML 源码,在 <head> 最顶部手动确保它唯一存在,别依赖模板自动注入或运行时 patch。
静态资源路径管理,<base> 是补丁,不是解法
硬塞 <base> 容易掩盖路径管理混乱的本质问题。尤其在多环境(开发/测试/生产)、CDN、SSR 混合部署时,它极易和构建工具配置冲突:
- Webpack 的
output.publicPath和<base href>同时设为/subpath/,可能让资源路径被拼两次(/subpath/subpath/js/app.js) - Vite 的
build.base已自动重写<script>和<link>,再加<base>属于冗余,还可能干扰new URL(, import.meta.url)的行为 - 手写的
<img src="./logo.png">不会被构建工具识别为可替换目标,哈希后缀加不上,缓存失效
真正可靠的做法:用构建工具统一注入路径(publicPath / build.base),HTML 中避免手写相对路径;必须写时,优先用 new URL('./logo.png', import.meta.url) 或 import logo from './logo.png',绕过 HTML 解析层的不确定性。



















