Skip to content

Instantly share code, notes, and snippets.

@JustThomas
Last active October 9, 2024 00:00
Show Gist options
  • Save JustThomas/141ebe0764d43188d4f2 to your computer and use it in GitHub Desktop.
Save JustThomas/141ebe0764d43188d4f2 to your computer and use it in GitHub Desktop.
WordPress Multisite: How to fix error "too many redirects"

WordPress Multisite: How to fix error "Request exceeded the limit of 10 internal redirects"

I am running a WordPress multisite network with sub-directory setup. When I check my error.log file, it is full of entries like this one:

Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'Limit InternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.

The problem was, in my case, one specific rewrite rule in the .htaccess file.

Problem description

If you open the network admin backend an go to the network admin page, then WordPress will suggest you some directives that you should put in your .htaccess file. In my case, these directives look like this:

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# uploaded files
RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
RewriteRule . index.php [L]

Imagine someone requesting a URL like http://example.com/wp-content/file.txt.

If file.txt exists on the server, then Apache will deliver the file to the client because of these directives:

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

If, however, file.txt does not exist as a static file on the server, these rules will not fire. But the following rewrite rule will attempt to rewrite the request URI:

RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]

But in our example, the requested URL http://example.com/wp-content/file.txt will just be rewritten to itself. Then, Apache tries to process this request again, going through the rules in the .htaccess file again and again. This results in the internal redirect loop.

Solution

What we had to do was to make sure that the aforementioned rewrite rule does not fire anymore for request URIs that begin with /wp-content/. One way to achieve this is to remove the question mark from this directive. So the new rewrite rule would look like this:

RewriteRule ^([_0-9a-zA-Z-]+/)(wp-(content|admin|includes).*) $2 [L]

Another possibility would be to add an additional rewrite condition before this rewrite rule. This would look like this:

RewriteCond %{REQUEST_URI} !^/wp-(content|admin|includes).*$
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]

If you go for this solution, then you might have to adapt the rewrite condition if your RewriteBase is something else than /.

new .htaccess file

I went for the first solution. Here's my new .htaccess file.

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# uploaded files
RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)(.*\.php)$ $2 [L]
RewriteRule . index.php [L]

I haven't had any of these errors since I applied these changes.

Bug report

This bug has been reported to the WordPress developers quite some time ago. I hope there will be a fix soon.

@dac514
Copy link

dac514 commented Sep 30, 2015

Ok. I messed around with this. I think I have something.

Instead of changing the rewrite rule, Change:

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

To:

RewriteCond %{ENV:REDIRECT_STATUS} 200 [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

This works by checking internal Apache variable %{ENV:REDIRECT_STATUS}. This variable is empty at the start of rewrite module but is set to 200 when first successful internal rewrite happens. This above condition says bail out of further rewrites after first successful rewrite and stops looping.

@retgef
Copy link

retgef commented Mar 7, 2017

Life saver! This one was beyond me.

@davidsword
Copy link

davidsword commented Mar 30, 2017

I just spent .. well I'm not willing to admit how long .. on trying to solve these massive 404->to->500 loops that were clogging my error log. This solved it and explain it. Shame on me for questioning every single line of my htaccess instead of thinking it could be a WordPress thing.

Thank you, thank you, thank you!

@PenguinzPlays
Copy link

Totally saved my bacon! Thanks again!!!

@sparrowxiao
Copy link

Very Helpful!
Thank you very much.

@mashcom
Copy link

mashcom commented May 1, 2018

I have to admit this has saved me from frustration. Thanks a lot.

@carloscangiano
Copy link

This solution worked for me.
Thank you very much!

@Kaji01
Copy link

Kaji01 commented Sep 21, 2018

One small bug I've got that I'll grant is something of an edge case, but if someone who knows more about .htaccess than I do can find a solution, I'm all ears!

Basically, I've got dev and qa subdomains in my shop, and each project gets a subdirectory off said domain. So in this case I have a setup like the following as the root of my WP Multisite install:

dev.test-domain.net/multisite_project/

What this means is that Multisite setup set the RewriteBase as /multisite_project/ instead of just a slash.

If I leave the RewriteBase alone, everything works normally on the main site, but the front end of sites with their own domains gets a 500 error for everything other than the front page. If I change the RewriteBase to /, sites with their own domains work perfectly, but the root site (which remains at dev.test-domain.com/multisite_project/) shows an error saying it cannot find "/multisite_project/sample-page/".

Any thoughts? Is there some conditional that can be put in to detect that case?

@jicao
Copy link

jicao commented Jan 25, 2019

RewriteCond %{ENV:REDIRECT_STATUS} 200 [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]

Saved my day ;)

@sidedwards
Copy link

+1 @jicao

@rafaelrodriguesdg
Copy link

Thank you very much sir!

@oolleegg55
Copy link

oolleegg55 commented Mar 23, 2020

RewriteCond %{ENV:REDIRECT_STATUS} 200 [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

Thank you. Helped a lot.

@remiduval
Copy link

Spent hours on this and this solution solved my problem. Thank you!

@IMFLEAN
Copy link

IMFLEAN commented Sep 29, 2020

This is my code, i need help for the same thing.

BEGIN WordPress

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !^/[0-9]+..+.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/.well-known/pki-validation/[A-F0-9]{32}.txt(?:\ Comodo\ DCV)?$
RewriteRule ^index.php$ - [L]

add a trailing slash to /wp-admin

RewriteCond %{REQUEST_URI} !^/[0-9]+..+.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/.well-known/pki-validation/[A-F0-9]{32}.txt(?:\ Comodo\ DCV)?$
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} !^/[0-9]+..+.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/.well-known/pki-validation/[A-F0-9]{32}.txt(?:\ Comodo\ DCV)?$
RewriteRule ^ - [L]
RewriteCond %{REQUEST_URI} !^/[0-9]+..+.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/.well-known/pki-validation/[A-F0-9]{32}.txt(?:\ Comodo\ DCV)?$
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).) $2 [L]
RewriteCond %{REQUEST_URI} !^/[0-9]+..+.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/.well-known/pki-validation/[A-F0-9]{32}.txt(?:\ Comodo\ DCV)?$
RewriteRule ^([_0-9a-zA-Z-]+/)?(.
.php)$ $2 [L]
RewriteCond %{REQUEST_URI} !^/[0-9]+..+.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/.well-known/pki-validation/[A-F0-9]{32}.txt(?:\ Comodo\ DCV)?$
RewriteRule . index.php [L]
RewriteCond %{HTTP_HOST} ^.*$
RewriteCond %{REQUEST_URI} !^/[0-9]+..+.cpaneldcv$

END WordPress

@KornelijusGlinskas
Copy link

It solved the issue. Huge thanks!

@martinmicevski
Copy link

Thank you very much!

@AmeliaAkh
Copy link

Hello,
@JustThomas Can the solution with deleting those two question marks cause security issues? I also need a solution for those redirect loops and 500 errors. If someone could help me with this for a fee with a custom code. I would greatly appreciate. With kind regards.

@jtfnascimento
Copy link

your guys are the best, thanks a lot

@vanbess
Copy link

vanbess commented Jul 14, 2022

This was a life saver! God bless!

@matugraphy
Copy link

I was asked to update a Wordpress website. It has multisite and the network sites are not reachable because of the same redirect error.
I have tried everything that I have found on Google. Nothing helps.

Can there be some mistake in this .htaccess file?

`RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]

uploaded files

RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]

add a trailing slash to /wp-admin

RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)(wp-(content|admin|includes).) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)(.
.php)$ $2 [L]
RewriteRule . index.php [L]`

@laguzfable
Copy link

Thank you so much, you saved my life!

@gabriel1680
Copy link

Thank you so much!!!

Brabo demais.

@FabioMontenegro
Copy link

You're the best!!!!!! It works

@atinluo
Copy link

atinluo commented Jan 30, 2024

Help me family, I have faced the same error, when I came to this page and found, redirection stopped, but frontend is showing me this error,
"Please refresh your browser or waiit and try again after some minutes!"
What can I do to solve that error message.
Here is my .htaccess codes

BEGIN WordPress Multisite

Using subfolder network type: https://wordpress.org/documentation/article/htaccess/#multisite

RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]

add a trailing slash to /wp-admin

RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteCond %{REQUEST_URI} !^/wp-(content|admin|includes).$
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).
) $2 [L]
RewriteRule . index.php [L]

END WordPress Multisite

@enkayexray
Copy link

Hello,
I am new to coding, so most of what we have here is Greek to me. I'm just a 59 year-old journalist trying to put his sites together. I have been working with multisites and hd no issues until I changed server. Migration was cool. Everything worked fine. Then my domains were transferred and after transfer was complete, I couldn't even get into my wordpress multisite backend.
I am told it's the code in my .htaccess file. I've reproduced here below. Could anyone help me figure out the problem? I am desperate.

BEGIN WordPress

RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] # END WordPress # php -- BEGIN cPanel-generated handler, do not edit # Set the “ea-php80” package as the default “PHP” programming language. AddHandler application/x-httpd-ea-php74___lsphp .php .php7 .phtml # php -- END cPanel-generated handler, do not edit

@Fexiven
Copy link

Fexiven commented Mar 19, 2024

While the notion of removing the "?" as a fix might initially seem viable, a closer inspection reveals its shortcomings. Let's delve into the intricacies:

^([_0-9a-zA-Z-]+/)(wp-(content|admin|includes).*) $2 [L]

This regular expression breakdown is as follows:

  • ^ anchors the pattern to the start of the string.
  • ([_0-9a-zA-Z-]+/) matches a string followed by a "/", such as "subpath/".
  • (wp-(content|admin|includes).*) matches "wp-content", "wp-admin", or "wp-includes", followed by any characters.

Matches:

subsite1/wp-content/plugins/
subsite2/wp-content/plugins/

Doesn't Match:
wp-content/plugins/

The "?" was initially included to optionally match the part before "wp-*", ensuring compatibility even if there's nothing preceding it.

However, an alternative approach i found proves to be more comprehensive:

RewriteRule ^(?:[_0-9a-zA-Z-]+/)?(wp-(?=content|admin|includes).*) $2 [L]

Here's why it excels:

  • (?:[_0-9a-zA-Z-]+/)? renders the preceding pattern (string followed by "/") optional without capturing it (no capture = little more performance, if im already doing it, then right).
  • (wp-(?=content|admin|includes).*) matches "wp-" followed by "content", "admin", or "includes", succeeded by any characters (this makes so the group (content|admin|includes) doesn't match the content, admin and includes twice.

Matches:

wp-content/plugins/
subsite1/wp-content/plugins/
subsite2/wp-content/plugins/

It's a more robust solution compared to simply removing the "?".

Alternatively, placing RewriteCond %{ENV:REDIRECT_STATUS} ^$ in front would work as well. However, I aimed for a more universal approach that could be compatible with non-Apache web servers.

https://regex101.com/r/gNqD8P/1 (initial)
vs
https://regex101.com/r/Tq9zik/1 (removed ?)
vs
https://regex101.com/r/n2bGBc/1 (mine)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment