An internal loop stays on the server: a per-directory rewrite restarts the request, the rule matches its own output (Stopping Rewrite Loops), and after LimitInternalRecursion (10) the client gets a
A redirect loop bounces the browser until it shows ERR_TOO_MANY_REDIRECTS. Two one-rule
files, each after RewriteEngine On, show both:
# File 1: every request to index.php/<path>, with no guard
RewriteRule ^(.*)$ index.php/$1 [L]
# File 2: "no trailing slashes", without the !-d guard of Section 2.6.3
RewriteRule ^(.+)/$ /$1 [R=301,L]$ curl -s -o /dev/null -w '%{http_code}\n' localhost:8111/products/42 # file 1
500
$ sudo tail -n 1 /var/log/apache2/example-error.log | grep -o 'AH00124.*redirects'
AH00124: Request exceeded the limit of 10 internal redirects
$ curl -sIL --max-redirs 1 localhost:8111/docs/ | grep -E '^(HTTP|Loc)' # file 2
HTTP/1.1 301 Moved Permanently
Location: http://localhost:8111/docs
HTTP/1.1 301 Moved Permanently
Location: http://localhost:8111/docs/LogLevel warn core:debug, as AH00124 suggests, logs one line per pass, from /products/42 to /index.php/index.php/.../products/42. Guard the rule with RewriteCond %{REQUEST_FILENAME} !-f or use [END]. In file 2, mod_dir restored the slash the rule removed, until curl 3,008 gave up with exit code 47; add the !-d guard rather than DirectorySlash Off, which the manual warns can expose a directory listing. An HTTPS rule behind Cloudflare 2 's Flexible mode (HTTP to the origin) loops the same way; see Canonical Host and HTTPS.