ESC

Type to search the knowledge base.

Base Element and Relative URLs

How <base href> rewrites relative URLs for links, scripts, and forms — powerful for static hosts, dangerous when set accidentally.

intermediate3 min read
  • html
  • base
  • urls

The <base> element sets a default URL and target for relative URLs in the document. One tag in <head> changes how every relative href, src, and form action resolves. That can fix asset paths on static hosts — or silently break navigation in SPAs.

Docs: MDN <base>, URL resolution.

Syntax

<head>
  <base href="https://cdn.example.com/app/" />
  <!-- optional default target for links -->
  <!-- <base target="_blank" /> -->
</head>

Rules:

  • At most one <base> with an href should be used; first wins in practice per spec nuances — don’t stack them.
  • Must live in <head>.
  • href should typically end with / when it represents a directory.

Resolution examples

<base href="https://example.com/docs/" />

<a href="guide.html">Guide</a>
<!-- → https://example.com/docs/guide.html -->

<img src="/logo.svg" alt="" />
<!-- root-relative: still https://example.com/logo.svg — leading / is path-absolute -->

<img src="images/a.png" alt="" />
<!-- → https://example.com/docs/images/a.png -->

<a href="https://other.test/x">External</a>
<!-- absolute URLs ignore base -->

Root-relative paths (/foo) resolve against the origin, not the base path. Document-relative paths (foo, ./foo, ../foo) use the base URL.

Forms and scripts

<base href="https://api.example.com/v1/" />
<form action="submit">…</form>
<!-- posts to https://api.example.com/v1/submit -->

<script src="app.js"></script>
<!-- loads https://api.example.com/v1/app.js — often not what you want -->

A base aimed at a CDN can break same-origin API relative calls if you’re not careful. Prefer absolute paths or explicit absolute URLs for APIs.

SPA and router footguns

Client routers that use relative links assume the document URL. With <base href="/app/">:

<a href="settings">Settings</a>
<!-- resolves under /app/ -->

But some bundlers inject scripts with relative paths that depend on base — Vite/Webpack expose base config for this reason. Mismatch between build base and HTML <base> causes blank pages.

When to use it

Scenario Verdict
Static site hosted in a subpath /blog/ Often yes — align with build base
Marketing site at domain root Usually unnecessary
Third-party HTML email Different rules; often avoid
Temporary debug hack in production No

Alternatives

  • Configure bundler base / publicPath
  • Use root-relative asset URLs
  • <link rel="…"> absolute CDN URLs for vendors

Interview out-loud

“<base href> sets the resolution base for relative URLs in the document. Root-relative paths still use the origin root. It’s useful for apps deployed under a subpath, but a wrong base breaks scripts, forms, and links. I keep a single base in head and align it with the bundler’s public path.”

Footguns

  1. Forgetting the trailing slash on directory bases.
  2. Base pointing at a CDN while forms should post to the app origin.
  3. target on base forcing every link to new tabs.
  4. Multiple base tags from concatenating layouts.
  5. In-page #hash links behaving oddly with certain base combinations — test.

Bundler alignment example

Vite base: '/docs/' should match:

<base href="/docs/" />

or rely on absolute root paths generated by the bundler without a HTML base tag. Pick one strategy. Mixed relative CSS url(./img.png) plus an unexpected <base> is a classic “works on homepage only” bug when routes deepen (/docs/a/b/).

Further reading

Related guides