Finally made some progress. In my case I found that the server was not following WordPress’s rewrite rules as expected.
The behavior I found which led to the redirect loop can be seen using cURL from the command line:
$ curl -Ikv https://dev.example.com/wp-login.php
* About to connect() to dev.example.com port 80 (#0)
* Trying 127.0.0.1... connected
* Connected to dev.example.com (127.0.0.1) port 80 (#0)
> HEAD /wp-login.php HTTP/1.1
> User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.15 libssh2/1.2.6
> Host: dev.example.com
> Accept: */*
>
< HTTP/1.1 302 Moved Temporarily
HTTP/1.1 302 Moved Temporarily
< Date: Tue, 28 May 2013 02:54:40 GMT
Date: Tue, 28 May 2013 02:54:40 GMT
< Server: Apache
Server: Apache
< WWW-Authenticate: Basic realm="Members Area"
WWW-Authenticate: Basic realm="Members Area"
< X-Pingback: https://dev.example.com/xmlrpc.php
X-Pingback: https://dev.example.com/xmlrpc.php
< Expires: Wed, 11 Jan 1984 05:00:00 GMT
Expires: Wed, 11 Jan 1984 05:00:00 GMT
< Cache-Control: no-cache, must-revalidate, max-age=0
Cache-Control: no-cache, must-revalidate, max-age=0
< Pragma: no-cache
Pragma: no-cache
< Location: https://dev.example.com/wp-login.php
Location: https://dev.example.com/wp-login.php
< Vary: Accept-Encoding
Vary: Accept-Encoding
< Content-Type: text/html; charset=UTF-8
Content-Type: text/html; charset=UTF-8
* no chunk, no close, no size. Assume close to signal end
<
* Closing connection #0
The 302 Moved Temporarily response (instead of 401 Unauthorized) suggested that the rewrite rules were not properly ending before the request was sent to WordPress. I confirmed this by disabling all rewrite rules and then selectively reenabling them until I got to the last one (where everything is sent to /index.php
).
In my case I found from the Apache error log that the request was being internally rewritten to a different filename and thus bypassing the %{REQUEST_FILENAME}
check:
[Mon May 27 19:44:30 2013] [error] [client 127.0.0.1] File does not exist: /home/me/dev.example.com/failed_auth.html
Adding the following to my .htaccess
resolved the redirect loop:
RewriteCond %{REQUEST_URI} ^/(stats/|missing\.html|failed_auth\.html) [NC]
RewriteRule . - [L]
Your case may vary. I suggest inspecting the Apache error and rewrite logs for errors similar to the one above.
Your cURL should look like the following once you’ve pinned it down:
$ curl -Ikv https://dev.example.com/wp-login.php
* About to connect() to dev.example.com port 80 (#0)
* Trying 127.0.0.1... connected
* Connected to dev.example.com (127.0.0.1) port 80 (#0)
> HEAD /wp-login.php HTTP/1.1
> User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.15 libssh2/1.2.6
> Host: plugindev.danieltwc.com
> Accept: */*
>
< HTTP/1.1 401 Authorization Required
HTTP/1.1 401 Authorization Required
< Date: Tue, 28 May 2013 02:57:57 GMT
Date: Tue, 28 May 2013 02:57:57 GMT
< Server: Apache
Server: Apache
< WWW-Authenticate: Basic realm="Members Area"
WWW-Authenticate: Basic realm="Members Area"
< Vary: Accept-Encoding
Vary: Accept-Encoding
< Content-Type: text/html; charset=iso-8859-1
Content-Type: text/html; charset=iso-8859-1
* no chunk, no close, no size. Assume close to signal end
<
* Closing connection #0