Because, since the redirect is unconditional, you end up redirecting again after the URL has been rewritten to index.php
(the WordPress front-controller).
When you request /somepage.php
:
- You are redirected to
/somepage
(by the first rule). The redirect response is sent back to the client.
- On the second request,
/somepage
is internally rewritten to /index.php
by the last rule. The rewriting engine then starts over (in a directory context)...
/index.php
is redirected to /index
(by the first rule). The redirect response is sent back to the client.
- On the third request
/index
is internally rewritten to /index.php
by the last rewrite. The rewriting engine then starts over...
- Goto 3 (stuck in an endless redirect-loop).
In a directory context (like .htaccess
) the rewriting engine does not simply make a single pass through the script. It loops until the URL passes through unchanged. (Unless you use the END
flag on Apache 2.4, or an external 3xx redirect occurs.)
Changing to remove .html
works OK because you are rewriting to /index.php
, which doesn't end in .html
, so the redirect directive (that removes .html
) does not match.
To resolve this you need to avoid redirecting the rewritten request. You can do this by either:
using the END
flag (Apache 2.4+) on the last rewrite, instead of L
to prevent any further loops of the rewrite engine. Although you should avoid changing the stock WordPress directives (see below), so this may not be the preferred option. This also does not work on Apache 2.2.
Or, check for the .php
extension against THE_REQUEST
server variable (which contains the initial line of the HTTP request headers and does not change when the request is rewritten). For example:
# Remove ".php" extension on "direct" (not rewritten) requests only
RewriteCond %{THE_REQUEST} [A-Z]{3,7}\s/[^?]+\.php(?:\?|\s|$) [NC]
RewriteRule (.+)\.php$ /$1 [R=301,L,NC]
Or, check the REDIRECT_STATUS
environment variable, which is empty on the initial request and set to 200 (as in 200 OK HTTP status) on the first successful rewrite (this is simpler than the rather more complex regex above). For example:
# Remove ".php" extension on "direct" (not rewritten) requests only
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule (.+)\.php$ /$1 [R=301,L,NC]
However, you should not edit the code inside the # BEGIN WordPress
section, since WordPress itself tries to maintain this and may overwrite this code later. This rule needs to go before the # BEGIN WordPress
comment marker. You do not need to repeat the RewriteEngine On
directive that appears later in the file (in the WordPress section).
You will need to clear your browser cache before testing, since the erroneous (permanent) redirect will likely have been cached by the browser. Test first with 301 (temporary) redirects to avoid caching issues.
However, this alone does not allow you to access .php
files without the .php
extension. Since the extensionless URL will need to be internally rewritten back to the .php
file.