Services

We build
high-quality websites
.

We are experts in creating websites that resonate with and convert

Let's work together!

Get a free consultation with our experts

Why us?

Behind every great website is a
great team
.

We have built 150+ websites for marketers and global brands.

Website

Latest
insights
.

A decade in the industry. 150+ websites built, optimized, and operated. Here we share what we’ve learned.

Case Study

Website migration from full-code to WordPress low-code solution

Read more
Read more
Services

Services

We build
high-quality websites
.

We are experts in creating websites that resonate with and convert

Let's work together!

Get a free consultation with our experts

About us

Why us?

Behind every great website is a
great team
.

We have built 150+ websites for marketers and global brands.

Insights

How a Single Extra Slash Caused Random Redirect Loops – and How We Traced It to the CDN

Table of Contents

Some website problems only show up for some visitors, on some devices, some of the time. They load perfectly on your own screen, so they are easy to wave away – and hard to catch. Recently, one of these landed on a client’s site in the middle of a product launch.

The difference wasn’t the page. It was one invisible character in the address some visitors were arriving with – and a caching default that turned it into a loop.

What happened?

The report came in the middle of a launch: the new page was throwing ERR_TOO_MANY_REDIRECTS for a portion of visitors. High stakes, and worse – we couldn’t reproduce it. It loaded every single time for us.

So we went to the source. We asked the origin server directly, bypassing every layer in front of it:

curl -I --resolve client.com:443:<origin-ip> https://client.com/en/page/
HTTP/2 200

A clean 200. We tried the www version, the no-trailing-slash version, the plain-HTTP version, a dozen browsers and devices. All 200. The server’s access log agreed: for that URL, it had returned nothing but 200s since the page went live. It had never issued a single redirect.

A page that loops for some visitors, on a server that never redirects it. So where was the redirect coming from?

What we found

The answer was one layer up, at the CDN – Cloudflare – and it came down to a single extra slash.

A small stream of requests was reaching the page with a doubled slash after the language prefix:

/en/page/     <- what everyone types
/en//page/    <- what some links and tools were generating

By default, Cloudflare tidies up doubled slashes before it looks up its cache – so both of these point at the same cached entry. But it forwards the original, messy address to the server unchanged. (Two settings govern this: Normalize incoming URLs is On by default, and Normalize URLs to origin is Off.)

WordPress sees /en//page/, decides the correct address is /en/page/, and answers with a redirect. Harmless on its own. The problem was what that redirect looked like next to the real page:

# the doubled-slash request - a REDIRECT, and cacheable for an hour
GET /en//page/
HTTP/2 301
location: https://client.com/en/page/
cache-control: max-age=3600
cf-edge-cache: cache

# the real page - a 200 that is never cached
GET /en/page/
HTTP/2 200
cache-control: no-store, no-cache, must-revalidate

Cloudflare cached that 301 – and because it had already stripped the doubled slash for its cache key, it filed the redirect under the clean address. From there:

  • A visitor requests /en/page/ – perfectly normal.
  • Cloudflare serves the cached 301 -> /en/page/.
  • The browser follows it – straight back to the same cached 301, at the same address.
  • Loop.

That is why it looked random. Cloudflare’s cache is split by region and device, so only visitors routed through a “poisoned” location hit it. And each entry expired after an hour, then re-poisoned on the next doubled-slash request – so it healed and broke on its own, which is precisely the behaviour that makes a bug impossible to pin down by hitting refresh.

It was never one page, either. Pulling the access log, we found 540 doubled-slash requests across 199 different URLs – including the language homepages. Any of them could quietly loop.

The fix

Cloudflare can’t be told to stop merging slashes in its cache key – that part is fixed behaviour. But it can be told to clean the address on the way to the server, too. One setting does it:

Rules -> Settings -> Normalization -> Normalize URLs to origin: On

With that on, the server receives /en/page/ even when the visitor sent /en//page/. It returns a normal 200, never generates the canonical redirect, and there is nothing cacheable left to poison. We verified it end to end by sending a tagged doubled-slash request and reading what the server actually received:

sent to Cloudflare:   /en//page/?check=1
arrived at server:    GET /en/page/?check=1   ->   200

Single slash, 200, no redirect. Then we purged the four already-poisoned launch URLs (one per language) and confirmed each served the real page again:

LanguageBeforeAfter
ENredirect loop200 (cache HIT)
ESredirect loop200 (cache HIT)
ITredirect loop200 (cache HIT)
BRredirect loop200 (cache HIT)

Two things that cost us time, so they don’t cost you yours:

  1. Fix the cause before you purge, not after. Purging a poisoned URL clears it for about four minutes – then the next doubled-slash request rebuilds the loop. Turn on normalization first, purge second. Otherwise you’ll watch your fix “work” and then break again while you’re still looking at it.
  2. Don’t test a CDN cache with a hard reload. A forced reload – or cache: "reload", or a Cache-Control: no-cache header – tells Cloudflare to skip its own cache and fetch fresh from the server. You end up measuring the origin, seeing everything work, and concluding the bug isn’t real. To see what the cache is actually serving, request it normally:
   // "opaqueredirect" here means a redirect really is sitting in the cache
   fetch(url, { redirect: "manual" })

This one sent us chasing the wrong layer for longer than we’d like to admit.

Why intermittent problems deserve a closer look

No amount of refreshing the page would have found this. The bug lived between the visitor and the server, in a layer that answered us correctly every single time we asked it directly. “Works for me” wasn’t a sign the report was wrong – it was the most important clue about where the problem actually was.

Finding it meant treating each layer as a separate suspect – the browser, the CDN, the server – and getting each one to tell the truth on its own. The moment we compared what the server returned (a clean 200) with what visitors received (a redirect), the culprit had nowhere left to hide.

Spot problems early. Fix them fast. Especially the ones only some of your visitors can see – because those are the ones that go unreported the longest.

Running into something only a fraction of your visitors experience? Get in touch with us.

Picture of Martin Vančo

Martin Vančo

Martin is a curious full-stack developer who feels at home across the entire web stack. Hée handles everything from frontend implementation and user experience to backend logic, databases, and deployment. A former drummer who traded drumsticks for a keyboard, he brings rhythm, focus, and strong multitasking skills into his work. Always learning, always building.

FullStack Developer

Share this artcle

Let's work together!

Get a free consultation with our experts

More
Articles
.

Blog

Why Paid Traffic Was Getting the Slowest Version of the Website

Read more
Read more
Blog

How Website Monitoring Helped Us Detect a Performance Drop Before It Became a Bigger Problem

Read more
Read more
Blog

Cloudflare Blocks AI Crawlers by Default: What Changes on September 15?

Read more
Read more

Subscribe to our quarterly newsletter and receive latest insights.

Topics: Improving B2B websites, AI tools in web development, UX/UI, website marketing trends etc.

By submitting this form you agree to the processing of your personal data according to our .

Contact

Let's work together!

Get a free consulting call with our experts

Book a call with
Webgate founders

Thank you for your interest!

We will contact you soon.