Why every link on my site broke the moment I deployed it

< Back Home

This is a draft — the technical details are accurate to how this site works, but rewrite the intro and any "I felt…" parts in your own voice before publishing.

This site is rendered by a static site generator I wrote in Python. It walks a content/ folder, converts each Markdown file to HTML, drops it into a shared template, and writes the result to docs/. On my laptop it worked perfectly. Then I pushed it to GitHub Pages and every single link and image was broken.

The problem

Locally I served the site from its own root, so a link like href="/Nghi-Hoang-Page/images/nghi.jpg" resolved to exactly that. But GitHub Pages serves a *project* site under a subpath — my site lives at /Nghi-Hoang-Page/, not at /. So the browser was asking for mysite.github.io/images/nghi.jpg when the file was actually at mysite.github.io/Nghi-Hoang-Page/images/nghi.jpg. Every absolute path was off by one directory level.

What I tried first

My first instinct was to rewrite all the links in my Markdown to include the prefix. That "worked," but it was a bad idea: now the site only worked when deployed and broke locally, and if I ever changed the repo name I'd have to edit every file by hand.

The fix

The right place to solve this was in the generator, not the content. I added a basepath argument that gets threaded through page generation, and rewrote the root-relative paths at build time:

template = template.replace('href="/Nghi-Hoang-Page/', f'href="{basepath}')
template = template.replace('src="/Nghi-Hoang-Page/',  f'src="{basepath}')

Now build.sh passes /Nghi-Hoang-Page/ for production, and the default is / for local testing. The content stays clean and portable, and the environment-specific detail lives in one place.

What I took away from it

The bug that only shows up in one environment is usually a sign that an assumption is baked into the wrong layer. Moving the base-path logic out of the content and into the build step made the whole thing simpler, not just correct. It's a small change, but it's the kind of thing I now look for first when something works in one place and not another.