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

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
When I hit http://angular.wlocal/ it should change the url to http://angular.wlocal/en-gb & serve index.html from the root folder on the server (without displaying the actual index.html file in the url)
If the requested file/folder does not exist eg. http://angular.wlocal/this-does-not-exist the url should change to http://angular.wlocal/en-gb/this-does-not-exist & serve index.html from the root folder on the server (/index.html)
If a file/folder exists eg. http://angular.wlocal/vendor.js - the url should not change & the file should be served relative to the root folder on the server ( /vendor.js )
This should also apply to directories. eg. http://angular.wlocal/css/style.css should serve /css/style.css on the the server.
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?

.... 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

How to redirect any URL to my index.html using apache?

I want my website to point at my index.html no matter what the URL is.
http://localhost/WebsiteName/
http://localhost/WebsiteName/whatever
http://localhost/WebsiteName/whatever/whatever-1/whatever-2/whatever-3/etc
By using this rewrite code in my apache config:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.html [L]
My URL works correctly when URL is any of these:
http://localhost/WebsiteName/
http://localhost/WebsiteName/whatever
But breaks when it is like this or further extended:
http://localhost/WebsiteName/whatever/
http://localhost/WebsiteName/whatever/whatever-1/whatever-2/whatever-3/etc
It acts as if there was another folder "whatever" in the directory whenever I use one of the URLs that break.
I don't want the URL to change, I just want it to point at my index.html no matter what it is.
Based off what you have there, all of those url's should go to your index.html. The index.html is assumed to be at the DOCUMENT_ROOT.
I'm not sure what your problem is, but maybe instead of having a condition for rewriting non-files. Stop rewriting when it's a file. I notice any changes on my website, with either config, but maybe it can work for yours.
RewriteEngine on
### If a uri leads to an actual file or directory (presumably with it's own index.html), then don't rewrite any further
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
### everything else
RewriteRule . /index.html [L]

Apache 2.4 .htaccess - Point All Requests to index.html With One Exception

I currently have the following .htaccess:
<IfModule mod_rewrite.c>
Options Indexes FollowSymLinks
RewriteEngine On
RewriteBase /production/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
The web server's document root is currently the /production folder referenced in the above (the .htaccess file is in the parent folder since /production is deleted and rebuilt with every code commit). The above will direct all traffic to my site to index.html.
I would like to make an exception to this. If the request is www.mydomain.com/specialrequest, I would like a PHP script called script.php in the parent folder to run.
To review, this is my /var/www/html directory:
-html
-production
-anotherfolder
-.htaccess
-script.php
Apache is pointing to /var/www/html/production and I would like all requests to go to the index.html file in that directory unless the request is /specialrequest - in which case, I would like script.php to run.
Apache is pointing to /var/www/html/production
So, /var/www/html/production really is defined as the DocumentRoot. And so your .htaccess file is located above the document root. And the file you want to rewrite to (upon receiving this special request) is also above the document root.
Unfortunately, you can't rewrite to a file that is above the document root using .htaccess (regardless of where that .htaccess file is located). This is because the RewriteRule substitution in per-directory .htaccess files takes a URL-path, so trying to rewrite to a URL above the document root is simply invalid. If, however, you had access to the server config (or virtual host) then you could rewrite to a filesystem path (including above the document root) - instead of a URL-path - using this method.
So, script.php would need to be within the document root in order to be able to do this using .htaccess. In which case, you could do something like:
RewriteEngine On
# Exception
RewriteRule ^specialrequest$ /script.php [L]
# Front controller
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
But in this case, script.php is would need to be located at /html/production/script.php (in the document root), not at /html/script.php (above the document root) - as in your current filesystem structure.
Incidentally, the RewriteBase /production/ directive in your .htaccess file is entirely superfluous. You're not using a relative path substitution anyway, but since /production is the document root then /index.html (a document root relative URL-path) will rewrite to /production/index.html anyway.
If, however, you could write this directly in your server config (or virtual host), then you could do what you require. For example:
RewriteEngine On
# Rewrite to filesystem path (in the server config)
RewriteRule ^/specialrequest$ /var/www/html/script.php [L]
# Front controller
RewriteRule ^/index\.html$ - [L]
RewriteCond %{LA-U:REQUEST_FILENAME} !-f
RewriteCond %{LA-U:REQUEST_FILENAME} !-d
RewriteRule ^/. /index.html [L]

htaccess - Redirect to subfolder without changing browser URL

I've a domain that contains a subfolder with the web app structure. I added a .htaccess on my root domain to point the public folder on my subfolder web app. It works fine, but when I type www.example.com the browser URL changes to www.example.com/subfolder/public, but I would like that it doesn't change.
This is my .htaccess:
RewriteEngine On
RewriteRule ^.*$ subfolder/public [NC,L]
EDIT
This first .htaccess is used to redirect on subfolder/public, where there is an other .htaccess that makes all the works.
Here the code of the second .htaccess located on www.example.com/subfolder/public/:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
Sorry, just realised what is happening. It has nothing to do with the second .htaccess file in the subdirectory, as mentioned in comments.
RewriteRule ^.*$ subfolder/public [NC,L]
Since public is a physical directory on the file system, you need to include a trailing slash when internally rewriting to that directory. Otherwise, mod_dir is going to try to "fix" the URL by appending a slash - that is where the external redirect is coming from. (mod_dir implicitly triggers an external redirect from subfolder/public to subfolder/public/.)
So, try the following instead in your root .htaccess file:
RewriteRule .* subfolder/public/ [L]
The important thing is the trailing slash. The anchors (^ and $) on the RewriteRule pattern are not required, since you are matching everything. And the NC flag is also not required for the same reason.
As always, make sure the browser cache is clear before testing.
UPDATE#1: The single directive above rewrites everything, including static resources, to the directory subfolder/public/ which then relies on the second .htaccess file in the subdirectory to correctly route the request. In order to allow static resources to be rewritten correctly (represented in the HTML as root-relative URL-paths, of the form "/js/myjs.js") then you will need additional directives in order to rewrite these.
For example, to specifically rewrite all .js and .css files to the real location in /subfolder/public/...
# Rewrite static resources
RewriteRule (.+\.(?:js|css))$ subfolder/public/$1 [L]
# Rewrite everything else to the "public" directory
RewriteRule .* subfolder/public/ [L]
UPDATE#2: To make the above more general, and to rewrite any static resource (images, PDFs, .txt, etc...) we can check for the existence of the file before rewriting, something like:
# Rewrite static resources
RewriteCond %{DOCUMENT_ROOT}/subfolder/public/$1 -f
RewriteRule (.+) subfolder/public/$1 [L]
# Rewrite everything else to the "public" directory
RewriteRule .* subfolder/public/ [L]
This will mean that if any .css does not exist it will be passed through to subfolder/public/.

mod_rewrite is ignoring rules in subdirectories

Wanted rewrite behaviour (internal rewrite!)
http://<subdomain>.domain.tld/<path> -> /<subdomain>/<path>
http://www.domain.tld/path/file.php -> /www/path/file.php
http://project.domain-tld/index.php -> /project/index.php
Folder structure:
/ root
.htaccess
/www www.domain.tld
index.php
/www
file.php
/foo
/bar
file.php
/project project.domain.tld
index.php
someRandomFiles
/somesubdomain somesubdomain.domain.tld
index.php
someRandomFiles
/anothersubdomain anothersubdomain.domain.tld
index.php
someRandomFiles
Full .htaccess
# Unicode
AddDefaultCharset utf-8
# Activate mod_rewrite
RewriteEngine on
RewriteBase /
# Subdomains
# Extract (required) subdomain (%1), and first path element (%3), discard port number if present (%2)
RewriteCond %{HTTP_HOST}<>%{REQUEST_URI} ^([^.]+)\.janbuschtoens\.de(:80)?<>/([^/]*) [NC]
# Rewrite only when subdomain not equal to first path element (prevents mod_rewrite recursion)
RewriteCond %1<>%3 !^(.*)<>\1$ [NC]
# Rewrite to /subdomain/path
RewriteRule ^(.*) /%1/$1 [L]
My .htaccess seems to work. You can live test it here:
http://test.janbuschtoens.de/
rewrites to /test/
http://www.janbuschtoens.de/
rewrites to /www/
But there is some strange behaviour in subdirectories. mod_rewrite seems to ignore the rule if the first directory in the requested path has the same name as the subdomain itself. For example:
http://www.domain.tld/foo/bar/file.php -> /www/foo/bar/file.php - Fine!
http://www.domain.tld/ -> /www/ - Fine!
http://www.domain.tld/www/ -> /www/ - Should be: /www/www/
http://www.domain.tld/www/www/ -> /www/www/ - Should be: /www/www/www/
For another live test:
http://test.janbuschtoens.de/ rewrites to /test/
http://test.janbuschtoens.de/test/ rewrites to /test/
It seems like the rule gets ignored.
This is the only good rule that I was able come up with, otherwise after initial rewriting (which is very easy) it goes into the loop (and that is the problem). For example: www.domain.com/www/123.png gets properly redirected into /www/www/123.png, but then goes to the next loop, where it get's redirected to /www/www/www/123.png and then again and again.
This rule ONLY gets invoked if FINAL filename DOES EXIST.
RewriteCond %{HTTP_HOST} ^(.+)\.domain\.com$
RewriteCond %{DOCUMENT_ROOT}/%1/$1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/%1/$1 -d
RewriteRule ^(.*)$ /%1/$1 [QSA,L]
For example: if you request www.domain.com/www/123.png, and file/folder WEBSITEROOT/www/www/123.png exist, then it will be rewritten, otherwise nothing.
The same here: if you request meow.domain.com/ .. but have no WEBSITEROOT/meow/ folder on your drive, it gets nowhere.
Please note, this still will not help much if you have subfolder with the same name as subdomain. For example: if you request www.domain.com it should be rewritten to WEBSITEROOT/www/ ... but if you also have WEBSITEROOT/www/www/ then (because of loop) it will be rewritten to WEBSITEROOT/www/www/ instead.
Unfortunately I have not found the way how to bypass it. If you wish -- you can try combining your rules with mine.

How to redirect "/" to "/home.html" only if the file "/index.html" does not physically exists?

I found a way to redirect (not load, but change the URL) "/" to "/home.html". And now I want to add a RewriteCond to avoid the redirection if the file "/index.html" exists.
I tried (without the comments), but it didn't worked :
# We check that we comes from "domain.tld/"
RewriteCond %{REQUEST_URI} =/
# We check that there is no index.html file at the site's root
RewriteCond %{REQUEST_URI}index\.html !-f
# We redirect to home.html
RewriteRule ^(.*)$ %{REQUEST_URI}home\.html [R=301,L]
Help me Obi-wan Kenobi... You're my only hope!
#Gumbo
It's a little bit more complicated than the above example. In fact, I manage both localhost and production development with the same .htaccess, so I tried something like this (following your answer) :
# Redirect domain.tld/ to domain.tld/home.html (only if domain.tld/index.html does not exists)
RewriteCond %{DOCUMENT_ROOT}index\.html !-f [OR]
RewriteCond %{DOCUMENT_ROOT}domain.tld/www/index\.html !-f
RewriteRule ^$ %{REQUEST_URI}home\.html [R=301,L]
I looked at the path returned by "%{DOCUMENT_ROOT}domain.tld/www/index.html" and it's exactly the path of my index.html file... nevertheless, it didn't worked too. :(
By the way, thanks for the "^$" astuce to avoid "%{REQUEST_URI} =/" ! \o/
Any idea why ?
The file check -f requires a valid file system path. But %{REQUEST_URI}index\.html is not a file system path but a URI path. You can either use -F instead to check the existence via a subrequest. Or use DOCUMENT_ROOT to build a valid file system path:
RewriteCond %{DOCUMENT_ROOT}/index.html !-f
RewriteRule ^$ %{REQUEST_URI}home.html [R=301,L]
Furthermore, the other condition can be accomplished with the pattern of RewriteRule. As you’re using mod_rewrite in a .htaccess file, the corresponding path prefix is stripped (in case of the document root directory: /) so that the remaining path is an empty string (matched by ^$).
if you have access to httpd.conf (apaches config file) you could set the default page in there.
Something like this:
<IfModule dir_module>
DirectoryIndex index.html home.html
</IfModule>
Based on the rule set that you posted in your update, you have a bit of a logical error going on. Right now, one of your RewriteCond conditions will always be true, since it seems likely that both index files will never exist in the same environment (one exists in development, the other in production). Since you've OR'ed them together, this means that your RewriteRule will never be ignored due to the condition block.
It's simple enough to fix (I've also added additional forward slashes, since DOCUMENT_ROOT typically doesn't have a trailing slash):
RewriteCond %{DOCUMENT_ROOT}/index.html !-f
RewriteCond %{DOCUMENT_ROOT}/domain.tld/www/index.html !-f
RewriteRule ^$ %{REQUEST_URI}home.html [R=301,L]
Note too that you could setup a virtual host with a local host name so that your development and production would be similar in terms of relative paths.