File Paths & URLs

HTML · Lesson 14 of 44 8 min read Updated

Every href, every src and every stylesheet link contains an address. Get it slightly wrong and you see a broken image, an unstyled page or a "404 Not Found" error. Most of these bugs come from a handful of misunderstandings about paths.

You already wrote simple relative links in HTML Links. In this lesson you will learn the parts of a URL, the four ways to write a path, exactly how the browser turns a path into a full address, how to handle spaces and Hindi characters, and why a site that works on your Windows laptop can break on a Linux server.

The parts of a URL

A URL (Uniform Resource Locator) is a full web address. It has up to six parts:

PartExampleMeaning
Schemehttps:How to fetch it (https, http, mailto, tel...)
Hostshop.example.inWhich server, by domain name
Port8080Which "door" on the server; usually hidden because https uses 443 by default
Path/books/class-12/physics.htmlWhich file or page on that server
Query?lang=hi&page=2Extra settings as name=value pairs
Fragment#chapter-3A place inside the page; never sent to the server

The browser can split any URL into these parts for you. Edit the address in the box and press the button.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>URL parts</title>
  <style>
    input { width: 100%; max-width: 560px; }
    td { padding: 3px 10px; border-bottom: 1px solid #ddd; }
  </style>
</head>
<body>
  <label for="address">Address</label><br>
  <input id="address" value="https://shop.example.in:8080/books/class-12/physics.html?lang=hi&page=2#chapter-3">
  <button type="button" id="split">Split it</button>
  <table id="parts"></table>

  <script>
    const show = () => {
      const table = document.querySelector("#parts");
      table.replaceChildren();
      try {
        const url = new URL(document.querySelector("#address").value);
        const rows = { protocol: url.protocol, hostname: url.hostname, port: url.port || "(default)",
                       pathname: url.pathname, search: url.search, hash: url.hash,
                       "lang value": url.searchParams.get("lang") };
        for (const [name, value] of Object.entries(rows)) {
          table.insertRow().append(Object.assign(document.createElement("td"), { textContent: name }),
                                   Object.assign(document.createElement("td"), { textContent: value }));
        }
      } catch {
        table.insertRow().insertCell().textContent = "That is not a complete URL. Did you forget https://?";
      }
    };
    document.querySelector("#split").addEventListener("click", show);
    show();
  </script>
</body>
</html>

Try removing https://: the browser can no longer understand the address on its own. That is exactly why href="www.example.in" does not link to another website.

Four ways to write a path

KindExampleStarts from
Absolute URLhttps://cdn.example.in/logo.svgNowhere: it is complete
Root-relative/css/style.cssThe root (top folder) of the current site
Document-relativeimages/logo.svg, ../index.htmlThe folder of the current page
Fragment or query only#fees, ?page=2The current page itself

Use absolute URLs for other websites. For your own site, document-relative paths work everywhere, even when you open files straight from your disk. Root-relative paths are shorter on big sites with deep folders, but they need a web server, as the warning in HTML Links explains.

How the browser resolves a path

The browser never requests ../index.html from a server. It first combines the path with the address of the current page, called the base URL, to make a complete URL. The rules are simple:

  • A plain name, or one starting with ./, means "in the same folder as this page".
  • A folder name followed by / goes down into that folder.
  • Each ../ goes up one folder. At the top of the site, extra ../ steps are ignored.
  • A leading / jumps to the root of the site.

Suppose the current page is https://www.example.in/courses/web/intro.html. Type paths into this tool and watch where they lead.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Path resolver</title>
  <style>
    input { width: 100%; max-width: 480px; margin-bottom: 8px; }
    output { display: block; font: 15px monospace; background: #f3f3f3; padding: 8px; }
  </style>
</head>
<body>
  <label for="page">Current page</label><br>
  <input id="page" value="https://www.example.in/courses/web/intro.html"><br>
  <label for="path">Path written in href or src</label><br>
  <input id="path" value="../python/intro.html">
  <output id="result"></output>
  <p>Try: <code>lesson-2.html</code>, <code>images/logo.svg</code>, <code>../../index.html</code>,
     <code>/css/style.css</code>, <code>#quiz</code>, <code>my notes.html</code></p>

  <script>
    const resolve = () => {
      try {
        const page = document.querySelector("#page").value;
        const path = document.querySelector("#path").value;
        document.querySelector("#result").textContent = new URL(path, page).href;
      } catch {
        document.querySelector("#result").textContent = "The current page must be a full URL.";
      }
    };
    document.querySelector("#page").addEventListener("input", resolve);
    document.querySelector("#path").addEventListener("input", resolve);
    resolve();
  </script>
</body>
</html>

Notice what happens with my notes.html: the browser quietly changes the space to %20. More about that below.

The trailing slash matters

The base folder is everything up to the last slash of the current address. So these two addresses behave differently:

  • On https://example.in/courses/, the path python.html becomes https://example.in/courses/python.html.
  • On https://example.in/courses, with no final slash, courses is treated as a file name, so python.html becomes https://example.in/python.html.

Put both addresses into the resolver to see it. If relative links work on one URL of a page but break on another, check the trailing slash first.

Paths inside CSS and JavaScript files

A path written inside a stylesheet, such as background-image: url("../images/pattern.svg"), is relative to the stylesheet file, not to the HTML page. A path given to fetch() in an ordinary script is relative to the page, while a path in a module import is relative to the module file.

CSS
/* This file is css/style.css, so ../ climbs out of the css folder */
.hero { background-image: url("../images/hero.svg"); }

Spaces and special characters

A URL may only contain a limited set of plain characters. Anything else, such as a space, ₹ or Devanagari letters, is sent as percent-encoded bytes: a % followed by two hexadecimal digits for each byte. A space becomes %20, and & becomes %26.

Some characters have a special job, so they must be encoded when they are part of a name: ? starts the query, # starts the fragment, & separates query pairs and % starts an encoding. In JavaScript, encodeURIComponent() encodes a value safely.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>URL encoding</title>
</head>
<body>
  <label for="text">Text to put in a URL</label>
  <input id="text" value="fee structure 2026.pdf">
  <p>Encoded: <code id="encoded"></code></p>
  <p>Try <code>Q&amp;A</code>, <code>₹1,299</code>, <code>50% off</code> or <code>नमस्ते</code>.</p>

  <script>
    const box = document.querySelector("#text");
    const update = () => {
      document.querySelector("#encoded").textContent = encodeURIComponent(box.value);
    };
    box.addEventListener("input", update);
    update();
  </script>
</body>
</html>

Browsers display Hindi and other scripts readably in the address bar, but send the encoded form to the server. Inside query strings created by forms, a space is often written as + instead of %20, as you saw in HTML Forms.

The simplest fix is to avoid the problem: name files with lowercase letters, digits and hyphens only, such as fee-structure-2026.pdf.

Upper and lower case

The scheme and domain name are not case-sensitive: HTTPS://EXAMPLE.IN is the same site as https://example.in. The path is a different story. Most web hosting runs on Linux, and Linux treats Logo.png, logo.png and logo.PNG as three different files.

This catches many beginners using XAMPP on Windows. Windows ignores case, so <img src="images/Logo.png"> happily shows logo.png on your laptop. After you upload the site, the same image is broken. Keep every file and folder name lowercase and type paths exactly as the files are named.

Paths that only work on your computer

HTML
<!-- Wrong: these point at YOUR disk, which visitors cannot reach -->
<img src="C:\Users\Priya\Desktop\site\images\logo.png" alt="Logo">
<img src="file:///C:/Users/Priya/Desktop/site/images/logo.png" alt="Logo">

<!-- Right: a path relative to the page, with forward slashes -->
<img src="images/logo.png" alt="Logo">

Editors sometimes insert full disk paths when you drag a file in. The page looks fine on your machine and broken for everyone else. Also note that URLs always use forward slashes /; the backslash \ belongs to Windows file paths.

Common mistakes

  • Capital letters in file names. Works on Windows, breaks on a Linux server. Use lowercase.
  • Spaces in file names. They become %20 and are easy to mistype. Use hyphens.
  • Disk paths such as C:\.... Use relative paths with forward slashes.
  • Forgetting that CSS paths start from the CSS file. url("images/a.svg") inside css/style.css looks for css/images/a.svg.
  • Ignoring the trailing slash. /courses and /courses/ resolve relative paths differently.

Key points

  • A URL has a scheme, host, optional port, path, query and fragment.
  • Absolute URLs are complete; root-relative paths start with /; document-relative paths start from the current page's folder.
  • The browser resolves every relative path against a base URL; ../ climbs one folder and the trailing slash decides the base folder.
  • Paths inside a stylesheet are relative to the stylesheet file.
  • Spaces and special characters are percent-encoded (%20); avoid them in file names.
  • Paths are case-sensitive on most servers, so name everything in lowercase.

Exercise

Your site has index.html, a folder css/ with style.css, a folder images/ with school-logo.svg, and a folder events/annual-day/ containing index.html. Write the <link> and <img> elements needed in events/annual-day/index.html to load the stylesheet and the logo, plus a link back to the home page, all using document-relative paths. Then write the CSS rule in style.css that uses school-logo.svg as a background image.

Show solution
HTML
<!-- events/annual-day/index.html -->
<link rel="stylesheet" href="../../css/style.css">
<img src="../../images/school-logo.svg" alt="Kendriya Vidyalaya Rohtak logo">
<a href="../../index.html">Back to the home page</a>
CSS
/* css/style.css */
.banner { background-image: url("../images/school-logo.svg"); }

The page is two folders deep, so it needs ../../ to reach the site root. The stylesheet is only one folder deep, and its paths start from its own folder, so it needs a single ../.

Quick Quiz

1. On the page https://example.in/shop/bags/index.html, where does href="../shoes/" lead?

2. Which part of a URL is never sent to the server?

3. Your images work in XAMPP on Windows but break after uploading to the host. What is the most likely cause?

4. What does a space in a URL path become?

Learn HTML with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call