Search code examples
apache.htaccesshttp-redirectmod-rewriteurl-rewriting

How can I create a redirect with .htaccess to correct path instead of page acess


I am making a multilingual dynamic site that creates a virtual path per language.

So french pages go to domain.com/fr/ english domain.com/en/page domain.com/fr/some/page but in reality these pages are in the base folder and /fr/ is converted to a query string.

This is all working with the following .htaccess:

RewriteEngine on
DirectorySlash Off # Fixes the issue where a page and folder can have the same name. See https://stackoverflow.com/questions/2017748

# Return 404 if original request is /foo/bar.php
RewriteCond %{THE_REQUEST} "^[^ ]* .*?\.php[? ].*$"
RewriteRule .* - [L,R=404]

# Remove virtual language/locale component
RewriteRule ^(en|fr)/(.*)$  $2?lang=$1 [L,QSA]
RewriteRule ^(en|fr)/$  index.php?lang=$1 [L,QSA]

# Rewrite /foo/bar to /foo/bar.php
RewriteRule ^([^.?]+)$ %{REQUEST_URI}.php [L]

My problem is that some sites (Like a Linkedin post) somehow remove the trailing / in the index page automatically. So if I put a link in my post of domain.com/fr/ somehow they make the link domain.com/fr even if it shows domain.com/fr/ but that 404's as domain.com/fr dosent exist.

So how can I redirect domain.com/fr to domain.com/fr/ or localhost/mypath/fr (There's many sites in my local workstation) to localhost/mypath/fr/.

I tried something like:

RewriteRule ^(.*)/(en|fr)$ $1/$2/ [L,QSA,R=301]
RewriteRule ^(en|fr)$ $1/ [L,QSA,R=301]

But that ended up somehow adding the full real computer path in the url: localhost/mypath/fr becomes localhost/thepathofthewebserverinmypc/mypath/fr/

I would very much appreciate some help as I have yet to find the right rule.

Thank you


Solution

  • RewriteRule ^(en|fr)$ $1/ [L,QSA,R=301]
    

    You are just missing the slash prefix on the substitution string. Consequently, Apache applies the directory-prefix to the relative URL, which results in the malformed redirect.

    For example:

    RewriteRule ^(en|fr)$ /$1/ [L,R=301]
    

    The substitution is now a root-relative URL path and Apache just prefixes the scheme + hostname to the external redirect. (The QSA flag is unnecessary here, since any query string is appended by default.)

    This needs to go before the existing rewrites (and after the blocking rule for .php requests).

    Note that the "internal rewrite" directives are correct to not have the slash prefix.


    Aside:

    DirectorySlash Off
    

    Note that if you disable the directory slash, you must ensure that auto-generated directory listings (mod_autoindex) are also disabled, otherwise if a directory without a trailing slash is requested then a directory listing will be generated (exposing your file structure), even though there might be a DirectoryIndex document in that directory.

    For example, include the following at the top of the .htaccess file:

    # Disable auto-generated directory listings (mod_autoindex)
    Options -Indexes
    

    UPDATE:

    this worked on the production server. As the site is in the server root. Would your know how can I also try and "catch" this on my localhost ? RewriteRule ^(.*)/(en|fr)$ /$1/$2/ [L,R=301] dosent catch but with only RewriteRule ^(en|fr)$ /$1/ [L,R=301] localhost/mypath/fr becomes localhost/fr/

    From that I assume the .htaccess file is inside the /mypath subdirectory on your local development server.

    The RewriteRule pattern (first argument) matches the URL-path relative to the location of the .htaccess file (so it does not match /mypath). You can then make use of the REQUEST_URI server variable in the substitution that contains the entire (root-relative) URL-path.

    For example:

    RewriteRule ^(en|fr)$ %{REQUEST_URI}/ [L,R=301]
    

    The REQUEST_URI server variable already includes the slash prefix.

    This rule would work OK on both development (in a subdirectory) and in production (root directory), so it should replace the rule above if you need to support both environments with a single .htaccess file.