I have a small PHP website with 3 pages. The page content is dynamically translated in Dutch or English (I take the language from the URL)
index.php
page-one.php
page-two.php
I want to achieve the following URL’s
https://www.example.com/ => https://www.example.com/en/ or nl/ depending browser language
https://www.example.com/en/ => index.php
https://www.example.com/en/page-one/ => page-one.php
https://www.example.com/en/page-two/ => page-two.php
https://www.example.com/nl/ => index.php
https://www.example.com/nl/page-one/ => page-one.php
https://www.example.com/nl/page-two/ => page-two.php
It works locally on my PC with WAMP with the following htaccess
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !(/$|.)
RewriteRule (.*) %{REQUEST_URI}/ [R=301,L]
RewriteCond %{HTTP:Accept-Language} ^nl
RewriteCond %{THE_REQUEST} /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /nl/$1 [L,R]
RewriteRule ^nl/(.*)$ /$1 [L]
RewriteCond %{THE_REQUEST} /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /en/$1 [L,R]
RewriteRule ^en/(.*)$ /$1 [L]
However, when I publish it on the shared webhosting (at OVH) the sub-folder with the page name points to the index file
OK https://www.example.com/ => https://www.example.com/en/ or nl/
OK https://www.example.com/en/ => index.php
NOK https://www.example.com/en/page-one/ => index.php
NOK https://www.example.com/en/page-two/ => index.php
same for the /nl/
The pages only show as follow
https://www.example.com/en/page-one/page-one/ => page-one.php
https://www.example.com/en/page-two/page-two/ => page-two.php
But also these URL’s works, which should not be the case
https://www.example.com/en/page-one/page-two/ => page-two.php
https://www.example.com/en/page-two/page-one/ => page-one.php
It seems that it runs line 10 and 14 of the htaccess twice.
How can I solve this?
2
Answers
Based on @MrWhite his reponse the following .htaccess does what is expected
This looks like a conflict with
MultiViews
(part of mod_negotiation). This would explain how this is able to work at all locally and the difference in behaviour on the live server (where I suspect MultiViews is not enabled). (Although it does not seem to explain how/en/page-one/page-one/
seemingly "works" on the live server? This would, however, work locally with MultiViews enabled. The same applies to/en/page-one/page-two/
– more on that below.)In your mod_rewrite directives you are not appending the
.php
extension at any point, so by themselves they cannot possibly work (unless you are requestingpage-one.php
– with the.php
extension – directly). So, it looks like you are relying on MultiViews (which effectively appends the file extension).It is MultiViews that allows something like this to "work". Although I would expect this to be the other way round. ie.
/en/page-one/page-two/
would serve/page-one.php
, notpage-two.php
as you suggest?What happens here is that your mod_rewrite rule internally rewrites a request for
/en/page-one/page-two/
to/page-one/page-two/
. MultiViews then initiates an internal subrequest to/page-one.php/page-two/
(/page-two/
is simply PATH-INFO) and/page-one.php
is served.You need to ensure that MultiViews is disabled. And then manually append the
.php
extension where appropriate. However, you’ve not stated how you are managing your static assets (JS, CSS, images, etc.)? Should these be subject to the same redirect/rewrites? Are these language specific also?I would assume you are linking directly to the static assets, so these should not be subject to URL-rewriting.
Try something like the following instead:
The
RewriteBase
directive in your original rule block was not required (and it’s not required here either).No need to check
THE_REQUEST
since we are aborting early when the request has already been internally rewritten (by checking against theREDIRECT_STATUS
environment variable – which is empty on the initial request and set the to HTTP status after the first successful rewrite).If on Apache 2.4 then you can use the
END
flag, instead ofL
, on the last two rules (both rewrites) and remove the first "Abort early" rule that checks theREDIRECT_STATUS
env var. TheEND
flag stops all processing so no "loop" occurs.Further improvements… consider redirecting the request if
index.php
,page-one.php
orpage-two.php
(ie. anything with a.php
extension) are requested directly.