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
- If the URL starts with a scheme (
https://), it's already absolute—use as-is - If the URL starts with
//, prepend the current scheme - If the URL starts with
/, combine with current scheme and authority - 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`);