Absolute vs Relative URLs

Understanding when and how to use different URL formats

Two Types of URLs

When referencing resources in HTML, CSS, or JavaScript, you have two choices:

  • Absolute URLs include the complete address, starting with the scheme
  • Relative URLs are partial addresses resolved against the current document's URL

Consider a file at https://example.com/blog/posts/2024/article.html. Here's how it might reference an image:

Type URL in HTML Resolves To
Absolute https://example.com/images/photo.jpg https://example.com/images/photo.jpg
Relative ../../images/photo.jpg https://example.com/blog/images/photo.jpg

Both reference valid resources, but they work differently and are appropriate in different situations.

Absolute URLs

An absolute URL contains all the information needed to locate a resource, regardless of where the reference appears. It includes at minimum a scheme and authority:

https://cdn.example.com/assets/logo.png

When to Use Absolute URLs

  • External resources: Linking to other websites
  • CDN assets: Files hosted on content delivery networks
  • API endpoints: When your frontend calls backend APIs
  • Canonical URLs: For SEO and sharing purposes
  • Email content: Images and links in HTML emails

Advantages

  • Unambiguous—always resolves to the same resource
  • Works regardless of where your HTML is served from
  • Required for cross-origin resources

Disadvantages

  • Longer and harder to read
  • Must update if domain changes
  • Hardcodes environment (dev vs production)

Relative URLs

A relative URL is resolved against a base URL—typically the current document's location. The browser combines them to form the final absolute URL.

There are several types of relative URLs:

Document-Relative URLs

These are relative to the current document's path:

Relative URL Meaning
image.jpg Same directory as current document
./image.jpg Same directory (explicit)
images/photo.jpg Subdirectory of current location
../image.jpg Parent directory
../../assets/image.jpg Two directories up, then into assets

Root-Relative URLs

These start with a forward slash and are relative to the domain root:

Root-Relative URL Always Resolves To
/images/logo.png https://[current-domain]/images/logo.png
/css/styles.css https://[current-domain]/css/styles.css
/ https://[current-domain]/

URL Resolution Rules

Let's trace how browsers resolve relative URLs. Assume the current page is:

https://example.com/blog/posts/2024/january/article.html

Resolution Examples

Relative URL Resolved Absolute URL
photo.jpg https://example.com/blog/posts/2024/january/photo.jpg
./photo.jpg https://example.com/blog/posts/2024/january/photo.jpg
../photo.jpg https://example.com/blog/posts/2024/photo.jpg
../../photo.jpg https://example.com/blog/posts/photo.jpg
../../../photo.jpg https://example.com/blog/photo.jpg
/photo.jpg https://example.com/photo.jpg
/images/photo.jpg https://example.com/images/photo.jpg

Resolution Algorithm

  1. If the URL starts with a scheme (https://), it's already absolute—use as-is
  2. If the URL starts with //, prepend the current scheme
  3. If the URL starts with /, combine with current scheme and authority
  4. Otherwise, resolve relative to the current document's path

The Base Tag

The <base> element changes how relative URLs are resolved within a document. It specifies a different base URL than the document's actual location.

<head>
  <base href="https://cdn.example.com/assets/">
</head>
<body>
  <!-- This image will load from https://cdn.example.com/assets/images/logo.png -->
  <img src="images/logo.png" alt="Logo">
</body>

Base Tag Gotchas

  • Affects all relative URLs in the document, including anchors
  • An anchor like <a href="#section"> might not work as expected
  • Only one <base> element is allowed per document
  • Must appear before any elements with URLs

Protocol-Relative URLs

You may encounter URLs starting with // (two slashes):

//cdn.example.com/script.js

These inherit the current page's protocol. On an HTTPS page, this becomes https://cdn.example.com/script.js. On HTTP, it becomes http://cdn.example.com/script.js.

Why to Avoid Them

Protocol-relative URLs were popular when sites supported both HTTP and HTTPS. Today:

  • HTTPS should be used everywhere
  • They break when opening HTML files locally (file:// protocol)
  • They're no longer recommended by Google and other authorities

Common Deployment Mistakes

Understanding relative URLs helps avoid these frequent problems:

Problem 1: Works Locally, Breaks on Server

Your site works at http://localhost:3000/ but breaks when deployed to https://example.com/myapp/.

Cause: Root-relative URLs like /images/logo.png resolve to the domain root, not your app's subdirectory.

Solution: Use document-relative URLs, or configure your build tool to handle the base path.

Problem 2: Nested Pages Break

Your homepage at /index.html works, but /blog/post.html has broken images.

Cause: Document-relative URLs like images/logo.png resolve differently depending on the document's location.

Solution: Use root-relative URLs (/images/logo.png) for shared resources.

Problem 3: CSS Background Images

CSS url() values are relative to the CSS file's location, not the HTML document.

/* In /css/styles.css */
.hero {
  /* This looks for /css/images/bg.jpg, not /images/bg.jpg */
  background: url('images/bg.jpg');

  /* Use root-relative instead */
  background: url('/images/bg.jpg');
}

Best Practices

For Site-Wide Assets

Use root-relative URLs for resources shared across your site:

<link rel="stylesheet" href="/css/main.css">
<script src="/js/app.js"></script>
<img src="/images/logo.png" alt="Logo">

For Local Resources

Use document-relative URLs for resources specific to a section:

<!-- In /blog/posts/my-article.html -->
<img src="screenshot.png" alt="Screenshot">
<a href="related-post.html">Related</a>

For External Resources

Always use absolute URLs:

<script src="https://cdn.example.com/library.js"></script>
<a href="https://external-site.com">External Link</a>

For APIs and Dynamic Content

Configure base URLs through environment variables:

// Configuration-based approach
const API_BASE = import.meta.env.VITE_API_URL || '/api';
fetch(`${API_BASE}/users`);