htaccess: add path parameter (country code) to URL & serve index.html

Viewed 229

I want to add a country code 'en-gb' to an Angular application through htaccess. The folder 'en-gb' does not exist. I only want this to show up in the url & serve index.html

My .htaccess

<IfModule mod_rewrite.c>
    RewriteEngine On
   
    #does the folder AND file exist? (ie. /index.html & css/style.css)
    #yes - serve it & pop out of htaccess
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
    RewriteRule ^ - [L]

    #does the url already have a country flag set?
    #no - add country flag (ie. http://angular.wlocal/ -> http://angular.wlocal/en-gb)
    RewriteCond %{REQUEST_URI} !/en-gb
    RewriteRule ^(.*)$ en-gb/$1
    
    #if file folder not found, serve index.html (ie. http://angular.wlocal/en-gb/this-does-not-exist -> should serve index.html)
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . index.html [L]

</IfModule>

My Vhosts

<VirtualHost 192.168.56.1:80>
    DocumentRoot "C:\Users\Dev\Documents\angular-10\dist"
    ServerName angular.wlocal
    ServerAlias angular.wlocal
    RemoteIPHeader X-Forwarded-For

    <Directory "C:\Users\Dev\Documents\angular-10\dist">
        DirectoryIndex index.html
    AllowOverride ALL
    Require all granted
    </Directory>
</VirtualHost>

If I remove below, the url changes, But I get The requested URL /en-gb/ was not found on this server.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.html [L]

Currently, the URL does not change to /en-gb - I think it's because RewriteRule . index.html [L] removes it.

How can I serve index.html from root while preserving this /en-gb rewrite?

1 Answers

.... preserving this /en-gb rewrite?

If you want the URL to change then this needs to be an external "redirect", not a "rewrite".

#does the folder AND file exist? (ie. /index.html & css/style.css)
#yes - serve it & pop out of htaccess
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

This currently isn't doing anything because the same request can't map to a file and a directory. These conditions need to be OR'd. It's simpler to test REQUEST_FILENAME in this instance, rather than concatenating two server vars.

Try the following instead:

RewriteEngine On

#does the folder OR file exist? (ie. /index.html & css/style.css)
#yes - pop out of htaccess and allow Appache to serve it
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

#does the url already have a country flag set?
#no - add country flag (ie. http://angular.wlocal/ -> http://angular.wlocal/en-gb)
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule !^en-gb/ /en-gb%{REQUEST_URI} [R=302,L]

#if file folder not found, serve index.html (ie. http://angular.wlocal/en-gb/this-does-not-exist -> should serve index.html)
RewriteRule . index.html [L]

Prefixing the /en-gb... you can avoid the condition that checks that it's not already prefixed with /en-gb by performing this check in the RewriteRule pattern and using the REQUEST_URI server variable instead of the $1 backreference in the substitution string. The check against the REDIRECT_STATUS environment variable simply ensures we are processing direct requests only and not rewritten requests to index.html.

The REDIRECT_STATUS env var is empty (not set) on the initial request from the client. However, when the request is rewritten to index.html (by the rule below) then this env var is updated to 200 (as in 200 OK HTTP status). So by checking this env var is empty (ie. ^$) we can avoid the rule being processed for internal rewrites (which would result in an endless loop in this case). An alternative is to simply use the END flag (Apache 2.4+) on the following rewrite, to prevent the rewrite engine looping, but this will likely need to be added on other rewrites in the future and the Apache version hasn't actually stated in the question.

Note that this is a 302 (temporary) redirect. If this is intended to be permanent then change this to a 301 - but only once you have confirmed that it works OK. Otherwise you can end up with caching issues.

There's no need to recheck that the request does not map to a file or directory in the last rule, since the first rule already performs this check.

The <IfModule mod_rewrite.c> wrapper is not required here and should be removed

If I remove below, the url changes, But

If you remove the last rule then your angular app is never called so you will indeed get a 404. But also, simply removing that last rule should not result in the URL changing since the earlier rule was still a rewrite and there are no other "redirects" here?

Related