How I Wasted My Day Debugging Redirects So You Don't Have To

Sian Dela CruzSian Dela Cruz
5 min read

If you’re not a fan of stories and are just looking for a quick way to help you on your redirects debugging, then you could skip to the part about the blog post. Otherwise, continue reading.

Introduction

There I was, finally almost wrapping up a task I’ve been doing for almost a month. I was tasked with upgrading our frontend codebase from Next 10 to Next 15 and React 16 to React 19. I tested everything and no bugs in sight. After weeks of headaches and new white hairs produced, I was finally confident in its stability. And so, I passed off my work to QA.

QA did their thing, and they found one bug. I looked at the bug and it was a single redirecting issue, shouldn’t be so complicated. I let out a huge sigh of relief. With all that work and possible uncertainties, only one bug was found that seemed to be simple enough. Little did I know what I was about to sign up for…

The Rabbit Hole: When Everything Seemed Right but Wasn’t

I got started right away. We had an existing legacy router and figured to look there. After a couple of trial and errors and well-placed console logs for suspect code blocks, no findings so far so I marked the legacy router itself “Clear”. Next, a file called redirect.js - a prime suspect. It had code that explicitly uses a 301 redirect, jackpot! Not even an hour in, it seems I already hit the gold mine. There were no other relevant codes even mentioning a 301 redirect in the code. Where else could it be, right? WRONG! I tested the redirect again and again, still the same thing. This was where things started to become blurry.

If not the part of the code that explicitly has 301 in it, and is called redirect.js, where should I even look? I honestly had no idea. I looked into our custom server.js entrypoint for our Next.js app, no luck with that. I tried moving away the page from our legacy router into Next’s default file-based router, nope. I tried upgrading possible related dependencies, nada! At this point, I was deep in the weeds - losing time, losing patience, and possibly losing my mind. I’ve been gaslighting myself that some things that are obviously unrelated could possibly be culprits. I mean, where else would I look? I even tried debugging our GraphQL infrastructure, and tried modifying it. In the end, I tried going back by batches in commits and testing the redirect issue in each version until I reached the version of our live production code… huh? It’s still not working? I tried in production, and even asked my teammates to try locally, it worked for them. What was happening? It all made sense earlier, nothing makes sense now.

The Blog Post That Saved Me (and Might Save You Too)

Just as I was about to give up, I found this article named “How to Debug Browser Redirects” by Hristiyan Dodov. He quoted:

“In the absense of cache control directives that specify otherwise, a 301 redirect defaults to being cached without any expiry date.”

Suddenly, everything made sense. I wasn’t crazy. My code wasn’t broken. My tools (especially the browser) weren’t lying — they were just too helpful, in a misleading way.

It was the browser cache all along that was keeping me away from getting answers.

How to Actually Debug Redirects

The best solution for my case that Dodov mentioned is using cURL in the terminal.

At first glance, it didn’t seem like anything special — just another terminal command I’d probably forget to use. But in reality, cURL ended up being the most objective, unfiltered, and browser-free way to know what was actually happening between my app and the client.

Why cuRL Helped Me

After hours of chasing false leads, I started revisiting the “obvious” fixes I had tried earlier - changing code, tweaking routes, updating configurations. I eventually confirmed the issue was related to redirect.js as I suspected, but the browser's aggressive caching prevented me from seeing my fixes work. But this time, instead of testing them in the browser (which had misled me before due to its aggressive caching of the 301 redirect), I tested using cURL:

// Command
curl -I http://localhost/profiles

// Response
HTTP/1.1 301 Moved Permanently
X-Powered-By: Express
Path=/; Max-Age=316224000
location: /profiles?default=1
Date: Wed, 23 Apr 2025 17:19:44 GMT
Connection: keep-alive
Keep-Alive: timeout=5

This command showed me the valuable information like Max-Age (The duration of the 301 cache in seconds). It helped me confirm if my fixes were working or not with confidence. The issue wasn’t my fix… it was that the browser still held onto an outdated redirect in its cache. I had solved the bug hours earlier — I just didn’t know it.

🔍 Other Handy cURL Flags You Might Want To Try

  • See the full redirect chain:

      curl -IL https://yourdomain.com/path
    
  • Mimic a specific user agent:

      curl -A "Mozilla/5.0" -I https://yourdomain.com/path
    
  • View detailed request/response activity:

      curl -v https://yourdomain.com/path
    

Conclusion

What started as a simple bug ended up being a full day's journey into the quirks of 301 redirects and browser caching. The lesson? When debugging redirects, don't trust your browser alone—it might be secretly caching responses that make you question your sanity.

Remember these key takeaways:

  • Browser caching of 301 redirects can be extremely persistent (potentially cached indefinitely!). If possible code your redirects to include Max-Age.

  • Use command-line tools like cURL to see the actual HTTP responses without browser interference.

  • Search more information about similar issues even beforehand to avoid falling into the debugging hell like me.

If you’d like to read more information about Dodov’s approach to debugging redirects (HINT: He has other tools he uses there as well), you can check out his article here.

Happy coding, and may your redirects always take you where you expect to go!

0
Subscribe to my newsletter

Read articles from Sian Dela Cruz directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sian Dela Cruz
Sian Dela Cruz