# Netlify force = true and the Redirect Loop You Ship By Accident

Adding force = true to a Netlify redirect rule feels harmless. On slash-normalizing rules it creates a self-referential redirect loop that curl -L hits the 50-redirect limit on. Here&#39;s what&#39;s actually happening and when force is safe to use.

Author: J.A. Watte
Published: April 18, 2026
Source: https://jwatte.com/blog/blog-netlify-force-true-redirect-loops/

---

Four sites this week. Same bug on all four. The site worked in a browser (sort of, Chrome quietly hides redirect loops in the network panel unless you look). In `curl -L`, it hit the 50-redirect ceiling and aborted. The culprit, every time, was a Netlify redirect rule that looked like this:

```toml
[[redirects]]
  from = "/glossary"
  to = "/glossary/"
  status = 301
  force = true
```

The intent is clear: normalize `/glossary` (no trailing slash) to `/glossary/` (trailing slash). The trailing-slash URL is where the real page lives. This rule should send `curl /glossary` to `/glossary/` once, serve the page, and be done. With `force = true`, it sends `curl /glossary/` to `/glossary/` every time, forever, until curl gives up.

## What force = true Actually Does

Netlify's redirect engine runs in two modes depending on whether `force = true` is set.

**Without force (default).** Netlify first checks if a real file exists at the requested path. If yes, serve the file. If no, evaluate redirect rules. This means redirect rules are fallbacks, they fire when nothing else matches. This is the behavior most people expect.

**With force = true.** Netlify evaluates the redirect rule before checking for files. If the rule matches, the redirect fires. If the destination also matches a redirect rule, that rule fires too. And so on.

The bug surfaces when you use force on a rule whose `to` value matches the rule's own `from` pattern. The slash-normalizing case is the classic example. Consider `from = "/glossary"` with `force = true`. The `from` pattern in Netlify matches prefix-style, and `/glossary/` technically starts with `/glossary`. Depending on your `pretty_urls` setting and how Netlify parses the rule, the redirect can fire on the normalized URL too, sending `/glossary/` to `/glossary/` in an infinite loop.

## The Curl Symptom

```
$ curl -IL https://example.com/glossary
HTTP/2 301
location: https://example.com/glossary/

HTTP/2 301
location: https://example.com/glossary/

HTTP/2 301
location: https://example.com/glossary/

[...]

curl: (47) Maximum (50) redirects followed
```

Fifty identical 301s with the same Location header. The browser often hides this because Chromium's redirect detection is forgiving, it may silently serve the destination after a handful of hops, but curl, wget, and most AI retrieval engines do not. They hit the limit and give up.

For an AI citation engine specifically, this is catastrophic. The bot fetches `/glossary/`, gets redirected to `/glossary/`, retries, gets redirected again, flags the URL as unreachable, and moves on. Your glossary page is invisible to AI retrieval even though it renders fine in a browser.

## Why Force Feels Useful

The `force = true` flag gets added because someone read the Netlify docs and learned that redirect rules are normally skipped when a real file exists. Then they hit a scenario where the rule needs to fire even though the file exists, for example, redirecting a deprecated page to a new one when the old file is still in the build output. Force is the right answer there.

The problem is that people then copy the `force = true` pattern to other rules where it's not needed. Slash-normalizing rules are the most common victim. The thinking goes "I want to make sure this redirect always fires" which is not what force means. Force means "fire this redirect even if the destination file exists." On a slash-normalizing rule, the destination file is exactly what you want to serve, that's the whole point of the redirect.

## The Correct Pattern

Slash normalization is what Netlify's `pretty_urls` setting is for. Set it once in `netlify.toml` and let Netlify handle it:

```toml
[build.processing]
  pretty_urls = true
```

With pretty URLs enabled, Netlify automatically redirects `/glossary` to `/glossary/` (or vice versa, depending on your build output). No explicit redirect rule needed. No force flag. No loop.

If for some reason you need an explicit rule, write it without force:

```toml
[[redirects]]
  from = "/glossary"
  to = "/glossary/"
  status = 301
```

Netlify will check if `/glossary/` exists as a real file. It does. It serves the file. One hop. No loop.

## When force = true Is Actually Correct

Force is the right answer when you need a redirect to fire even though a file exists at the destination path. Two real examples:

**1. Legacy content override.** You have an old page at `/about-us.html` in your build output that you can't remove for cache reasons, but you want traffic to go to `/about/`. A forced redirect overrides the static file:

```toml
[[redirects]]
  from = "/about-us.html"
  to = "/about/"
  status = 301
  force = true
```

**2. Geo or auth-gated content.** You want a page to serve different content to different users based on a header or cookie. The default Netlify behavior of "serve the file if it exists" would bypass your logic. Force ensures the rule fires every time:

```toml
[[redirects]]
  from = "/app"
  to = "/login/"
  status = 302
  force = true
  conditions = {Cookie = ["!authenticated"]}
```

In both cases, the destination is different from the source pattern in a way that prevents self-reference. No loop risk.

## The Audit Check

Every site I run now gets a redirect-loop check as part of deploy-preview. The bash one-liner is:

```bash
for url in /glossary/ /about/ /tools/ /contact/; do
  code=$(curl -s -o /dev/null -w "%{http_code}" \
    -L --max-redirs 3 "https://example.com$url")
  echo "$url -> $code"
done
```

If any URL reports a non-2xx code, I check the full redirect chain with `curl -IL` and look for repeating Location headers. If the same URL appears twice in the chain, something is looping.

I bake this into the post-deploy notification on every site in the network now. Catching a loop at deploy time is an order of magnitude cheaper than catching it weeks later when AI citations stop coming in.

## Why I Saw This on Four Sites at Once

I had cloned a `netlify.toml` template across the network months ago. The template included a redirect block with `force = true` on the slash-normalizing rule. I didn't notice at the time because the sites all rendered fine in a browser. The Link Graph tool caught it last week when it started flagging `/glossary/` on every site as "unreachable (redirect loop)." Four sites, same bug, same fix.

The fix was one line per site: delete `force = true` from the slash-normalizing rule. Commit, deploy, re-run the audit. Clean.

## The Short Version

- `force = true` on a Netlify redirect makes the rule fire even if the destination file exists. That's the whole difference from the default.
- On slash-normalizing rules (`/glossary` to `/glossary/`), force creates a self-referential loop. `curl -L` hits the 50-redirect limit; AI retrieval engines give up.
- Remove `force = true` from any redirect whose `to` value is a variant of the `from` pattern.
- Use `[build.processing] pretty_urls = true` for automatic slash normalization. No rule needed.
- Keep force only when you're overriding an actual static file on disk or gating content by condition.
- Add a post-deploy curl loop check to catch this at deploy time.


---

Canonical HTML: https://jwatte.com/blog/blog-netlify-force-true-redirect-loops/
RSS: https://jwatte.com/feed.xml
JSON Feed: https://jwatte.com/feed.json
Hero image: https://jwatte.com/images/blog-netlify-force-true-redirect-loops.webp
