File paths: why your image vanished
Your page works. The photo shows, the stylesheet applies, every link opens. You upload it to a server, load the same page on the same laptop, and the photo is a broken icon with a bit of alt text next to it.
Nothing about the file changed. What changed is who is resolving the path.
A path is directions, not an address
When you write src="photo.jpg", you have not told the browser where the file
is. You have told it how to get there from the page it is currently
reading. The browser takes the current page's address, throws away the file
name at the end, and joins your path onto what remains.
Say the browser is showing https://example.com/canteen/menu.html. It strips
menu.html, leaving https://example.com/canteen/, and asks the server for
https://example.com/canteen/photo.jpg.
That is the whole mechanism. Every path bug in this lesson is a case of the starting point not being where you assumed.
Going down into a folder
Real sites do not keep everything in one folder. Here is a small one:
site/
index.html
menu.html
images/
biryani.jpg
chai.jpg
styles/
main.css
From index.html, a file inside images/ is reached by naming the folder,
then a slash, then the file:
<img src="images/biryani.jpg" alt="A plate of biryani with raita">
<link rel="stylesheet" href="styles/main.css">
No slash at the front. images/biryani.jpg means "from here, go into
images". Read it left to right as directions and it stays clear.
Note there is nothing special about the names images or styles — they are
folders you made up. The browser has no idea what they are for.
../ goes up one level
Now suppose the site grows and you put the menu pages in their own folder:
site/
index.html
images/
biryani.jpg
styles/
main.css
menu/
lunch.html
dinner.html
From menu/lunch.html, writing href="styles/main.css" asks for
site/menu/styles/main.css, which does not exist. You are one level too deep,
so you have to climb out first. .. means the folder above this one:
<link rel="stylesheet" href="../styles/main.css">
<img src="../images/biryani.jpg" alt="A plate of biryani with raita">
<a href="../index.html">Back to the front page</a>
Read ../styles/main.css as: go up out of menu, into styles, take
main.css.
You can stack them. From a file two folders deep, ../../index.html climbs
twice. And dinner.html sitting next to lunch.html needs no prefix at all —
href="dinner.html" is enough, because they share a folder.
This is where most path confusion starts. The path is written relative to the
file that contains it, not relative to the site, and not relative to the
page the reader came from. Move lunch.html into a different folder and every
path inside it has to be recounted.
Root-relative paths start from the top
There is a way to stop counting. A path beginning with a single / is
root-relative: it starts at the root of the site, wherever the current
page happens to be.
<link rel="stylesheet" href="/styles/main.css">
<img src="/images/biryani.jpg" alt="A plate of biryani with raita">
<a href="/index.html">Back to the front page</a>
Those three lines are correct in index.html, in menu/lunch.html, and in a
page five folders deep. That makes root-relative paths the sensible choice for
things every page needs — the stylesheet, the logo, the navigation links.
The catch is the reason this lesson exists in the first place. Root-relative
paths need a server. If you open a file by double-clicking it, the address
is something like file:///D:/Projects/site/menu/lunch.html, and the "root"
the browser resolves /styles/main.css against is the root of your entire
hard disk — D:/styles/main.css. Nothing there.
So /styles/main.css can fail locally and work when deployed, while
../styles/main.css works in both. If you want to use root-relative paths,
run a local web server rather than opening files directly. Most editors have
one built in, and Python ships with one:
python -m http.server
Then load http://localhost:8000/ instead of double-clicking.
Windows says the case does not matter. The server disagrees.
This is the bug that ships. It passes every test you run, and breaks the moment real users load the page.
You save a photo from your phone. It arrives called Photo.JPG, with a
capital P and a shouting extension. You write the tag from memory:
<img src="images/photo.jpg" alt="The canteen counter at lunchtime">
On your laptop, it works. The photo appears. You look at it and move on.
Windows and macOS both use file systems that are case-insensitive by
default: ask for photo.jpg and you get Photo.JPG, because as far as those
systems are concerned they are the same name. Your browser asks the file
system for a file, the file system hands one over, everyone is happy.
Then you deploy. Almost every web server in the world runs Linux, and Linux
file systems are case-sensitive. Photo.JPG and photo.jpg are two
different names, in exactly the way Bilal and bilal are two different
words. The server looks for photo.jpg, finds no such file, and returns a
404. Your page shows a broken image to everyone except you.
The same thing happens to stylesheets, which is worse, because a missing stylesheet does not leave a broken icon behind. The page just renders as unstyled black text on white and you assume you have broken your CSS.
Worse still, this bites when you rename. Change photo.jpg to Photo.jpg on
Windows and Git may not even notice the change, because Git is asking the same
case-insensitive file system whether anything is different. You commit,
deploy, and the server still has the old name — or the new one, and now your
HTML is wrong.
The fix is a habit, not a setting:
- Lower case everything. File names, folder names, extensions. Every time.
- No spaces. Use a hyphen:
chai-stall.jpg, notchai stall.jpg. Spaces in a URL become%20, which is legal but ugly and easy to get wrong. - Copy the name, do not type it. Most of these bugs are typed from memory by someone who was sure.
- Rename files the moment you add them, before any HTML points at them.
Two paths that look almost identical:
<img src="images/Photo.JPG" alt="The canteen counter at lunchtime">
<img src="images/photo.jpg" alt="The canteen counter at lunchtime">
If your file is called photo.jpg, only the second one loads on a Linux
server. There is no browser setting that rescues the first, and there is no
warning while you develop.
Reading a broken path in the browser
When something does not load, open the browser's developer tools and look at the Network tab. A failed request shows up in red with a 404 next to it, and the URL it tried. That URL is the truth: it tells you exactly where the browser looked, which is usually enough to see where your counting went wrong.
If the URL contains file:/// and a path off your hard disk, you have a
root-relative path and no server. If it contains a folder name twice —
site/menu/menu/lunch.html — you have a relative path where you meant to go
up first. If it looks perfect, check the capital letters.
Which kind to use
For links between pages in a small site, relative paths are fine and they work
without a server. For shared assets — stylesheets, logos, the main navigation
— root-relative paths save you from recounting ../ every time you move a
file, provided you are running a server locally. Absolute URLs with a domain
belong only to other people's sites.
Whichever you choose, be consistent within a project. A site with a mixture is a site where nobody can tell at a glance whether a path is broken.
Next, images covers what goes in that src and why alt is
not optional. If a path in an href is what is failing you,
the links lesson covers the <a> element itself.
Prefer learning with a teacher?
WasiLearn Academy runs small-group classes covering this material, online and in Karachi.
See the classes