One of the most difficult to troubleshoot issues I've ever encountered with WordPress is a redirect loop that happens when attempting to access any WordPress multisite site/wp-admin pages. Apparently, redirect loops have been a persistent problem in some installations. WordPress.org has, as of this article, 300 threads dedicated to the issue. There are quite a few posts that describe solutions that resolve the redirect issue when caused by www vs. non-www or ssl vs. non-ssl domains. There are also quite a few that indicate problems with a site's .htaccess file. Non of these solutions are applicable to the problem I encountered, which appeared quite suddenly and inexplicably one afternoon last week. I was able to log into my sites in the morning. In the afternoon, the redirect loop occurred. It drove me crazy for days and led to more than one argument with my host's support team, for which I apologize.
Of course, one of the first things I thought of was, “I've been hacked!" I went through all the steps required when a WordPress site gets hacked to no avail. I confirmed .htaccess and wp-config were right. I even dumped the database to search for redirects in the data. There was nothing to be found and the problem wouldn't go away.
Diagnosing the Redirect Loop
A redirect loop is by its very nature very difficult to diagnose because we can't see what the browser is seeing when a page is loading. Then I came across a great website for diagnosing redirect loops: http://redirectdetective.com/. Simply type in the offending web page URL and the site returns a display of what the browser is doing when attempting to load the page.
When any browser communicates with a web server, it executes the following cycle:
- Lookup the IP address from a domain name server (DNS).
- Open an IP socket connection to that IP address.
- Write an HTTP data stream through that socket.
- Receive a response HTTP data stream back from the Web server.
The response data stream contains status codes whose values are determined by the HTTP protocol in use. The browser parses this data stream for useful information. The 302 error occurs when the client receives an HTTP status code that it recognizes as ‘302'. The browser receives a 302 error code when the Web server thinks that the URL has been temporarily redirected to another URL. The browser then immediately retries the alternate URL. If you look at the redirect error I was receiving as reported by Redirect Detective, the redirect was redirecting to itself. Under normal circumstances, this is not possible and it triggered the redirect loop. But this still doesn't get us to the root cause. Something else is going that create this redirect loop.
Determining the Root Cause
At this point, I know the problem is isolated to the wp-admin directory. The network admin page and all externally facing pages are working correctly. I can even login to the individual sites by directly calling wp-login. I just can't load the dashboard. Then I notice that every time I run the wp-admin page through the Redirect Detective site, the error log in the wp-admin directory was growing exponentially. Here is a sample of what the error log contained:
[13-Oct-2014 11:28:44 America/Chicago] PHP Warning: PHP Startup: Unable to load dynamic library '/usr/local/lib/php/extensions/no-debug-non-zts-20090626/suhosin.so' - /usr/local/lib/php/extensions/no-debug-non-zts-20090626/suhosin.so: undefined symbol: php_mb_gpc_encoding_detector in Unknown on line 0
[13-Oct-2014 11:28:45 America/Chicago] PHP Warning: PHP Startup: Unable to load dynamic library '/usr/local/lib/php/extensions/no-debug-non-zts-20090626/bcmath.so' - /usr/local/lib/php/extensions/no-debug-non-zts-20090626/bcmath.so: cannot open shared object file: No such file or directory in Unknown on line 0
[13-Oct-2014 11:28:45 America/Chicago] PHP Warning: PHP Startup: Unable to load dynamic library '/usr/local/lib/php/extensions/no-debug-non-zts-20090626/mysql.so' - /usr/local/lib/php/extensions/no-debug-non-zts-20090626/mysql.so: cannot open shared object file: No such file or directory in Unknown on line 0
[13-Oct-2014 11:28:45 America/Chicago] PHP Warning: PHP Startup: Unable to load dynamic library '/usr/local/lib/php/extensions/no-debug-non-zts-20090626/mysqli.so' - /usr/local/lib/php/extensions/no-debug-non-zts-20090626/mysqli.so: cannot open shared object file: No such file or directory in Unknown on line 0
[13-Oct-2014 11:28:45 America/Chicago] PHP Warning: Module 'imagick' already loaded in Unknown on line 0
These errors are saying that php couldn't load! Something was blocking php. I checked all directory and file permissions. Everything was right. My next thought was, “Maybe the host updated their firewall rules and that stopped php from executing in wp-admin. I contacted support again, but knowing the turn around time can vary, I thought, “What have I got to lose if I install a php.ini file directly in the wp-admin directory?" This could force php to load and hopefully solve the problem.
Well, was I get surprised when I discovered that there was already a php.ini file the wp-admin directory. The file was timestamped from early 2013. So instead of adding a new php.ini to the wp-admin directory, I renamed the one that was already there to php.old so it wouldn't execute. This solved the wp-admin redirect loop. There was no actual redirect. The wp-admin pages couldn't execute. I have no idea where the php.ini file came from and as far as I know, there's no reason why there should be one in that directory. It may have been left over from an old security plugin that I no longer use, but I certainly can't rule out that it was placed there by someone wanting to do damage.