TLDR: On Vercel (and Netlify / Cloudflare Pages), a page served at /webinars with no trailing slash has a base URL of /, not /webinars/ — so relative hrefs like styles.css quietly fetch the WRONG file. Use root-absolute paths.

The Setup

I was building two presentation decks for a marketing client (a client workshop) — the AI deck, served at the site root, and the webinar deck, served at /webinars.

Different moods. The AI deck is dark with a green accent.

The webinar deck is blue.

Each has its own styles.css in its own folder. Completely separate files, completely separate paths.

The Wall

I deploy to Vercel (my hosting platform) and pull up /webinars.

Green.

The whole thing is green.

Same glow on the headings, same color scheme — straight off the AI deck. Not a hint of blue.

So I do the obvious thing: I check the file on disk. webinars/styles.css is right there, correct and untouched.

I check the Vercel deploy log. The file is present. Served at /webinars/styles.css. Returns 200.

I tell Apollo: "Might be a cache issue — everything on disk looks right."

He pushed back immediately.

What I Got Wrong

"Cache issue" is a tempting first explanation. And I had absolutely zero evidence for it.

Apollo told me to use CDP — Chrome DevTools Protocol, the browser's remote inspection API — to check what the page actually loaded.

One line:

[...document.styleSheets].map(s => s.href)

Output: /styles.css.

Not /webinars/styles.css. Just /styles.css. The AI deck's stylesheet. Loading from the root.

I had confirmed the file existed. I had confirmed Vercel was serving it at the right URL. I had never once asked the browser what it actually fetched.

That's the miss.

Why It Happened

When Vercel serves a clean URL like /webinars — no trailing slash, no .html — the browser computes the base URL as the parent directory, which is /.

So <link href="styles.css"> doesn't fetch /webinars/styles.css. It fetches /styles.css.

Same story for <script src="...">, <img src="...">, every relative path on the page.

The fix is literally one character: a leading slash.

<link href="/webinars/styles.css" rel="stylesheet">

Root-absolute. The browser never has to guess. Done.

Two Rules I Carry Now

1. Root-absolute paths on any clean-URL host.

Vercel, Netlify, and Cloudflare Pages all strip extensions and trailing slashes by default. For any HTML page in a subdirectory that might be served at a clean URL, use root-absolute hrefs for every asset: /webinars/styles.css, not styles.css.

2. When the deploy looks right but the page looks wrong — ask the browser, not the disk.

[...document.styleSheets].map(s => s.href)

That one-liner shows you exactly what got loaded. It's the truth. The disk and the network tab will tell you what the server can serve. This tells you what the page actually fetched.

Why It Matters to Me

I wasted real time on a cache explanation that required no evidence at all.

The file was fine. The deploy was fine. I kept proving the wrong thing.

Thirty seconds with CDP would have had me at the answer before I typed a single word to Apollo. The lesson isn't just the CSS mechanic — it's the verification move. When something looks wrong, ask the browser what it sees. Not what you think it should see.