Apache Rewrite rule to manage language redirections - regex

I am trying to structure a rewrite rule to implement redirection based on languages.
My directory structure is as follows:
.
├── .htaccess
├── assets
│   ├── css
│ │   └── master.min.css
│   ├── fonts
│   │   ├── fontawesome-webfont.eot
│   │   ├── fontawesome-webfont.svg
│   │   ├── fontawesome-webfont.ttf
│   │   ├── fontawesome-webfont.woff
│   │   ├── fontawesome-webfont.woff2
│ │   └── FontAwesome.otf
│   ├── img
│   │   ├── logo.svg
│   │   ├── slide1.jpg
│   │   ├── slide2.jpg
│   │   ├── slide3.jpg
│ │   └── slide4.jpg
│   └── js
│    └── scripts.min.css
├── de
│   ├── index.php
│   ├── sie.php
│   ├── uns.php
│   └── zusammen.php
├── en
│   ├── index.php
│   ├── together.php
│   ├── us.php
│   └── you.php
├── fr
│   ├── ensemble.php
│   ├── index.php
│   ├── nous.php
│   └── vous.php
└── it
    ├── index.php
    ├── insieme.php
    ├── noi.php
    └── voi.php
I have no root index.html or index.php file. I want to have the .htaccess redirect the user to one of the index files inside the language directories by sniffing the browser language and then redirecting the user to the appropriate language. The default language, when the browser's language cannot be sniffed should be French.
My current .htaccess file consists of the following:
Options -Indexes +FollowSymLinks
RewriteEngine On
RewriteRule ^(en|de|fr|it)/ - [L,NC]
RewriteCond %{HTTP:Accept-Language} ^fr [NC]
RewriteRule ^(.*)$ /fr/$1 [L]
RewriteRule ^(.*)$ /en/$1 [L]
RewriteRule ^(.*)$ /de/$1 [L]
RewriteRule ^(.*)$ /it/$1 [L]
This seem to function partially, but it isn't able to access the various assets such as images, javascripts, or fonts.
Once the page loads, thereafter, there shouldn't be any problems navigating and changing languages as I have used internal URLs that are directly linking to the specific pages in the appropriate directories.
Any help would be appreciated. Thank you.

You can detect and route using browser's languages. Place this code in root .htaccess
RewriteEngine On
RewriteRule ^(assets|en|de|fr|it)/ - [L,NC]
# detect browser language and capture first 2 chars
RewriteCond %{HTTP:Accept-Language} ^([a-z]{2}) [NC]
# current request is not pointing to a real file
RewriteCond %{REQUEST_FILENAME} !-f
# check if corresponding directory exists
RewriteCond %{DOCUMENT_ROOT}/%1/ -d
RewriteRule ^ %1%{REQUEST_URI} [L]
## default fr rule
# current request is not pointing to a real file
RewriteCond %{REQUEST_FILENAME} !-f
# current request is not pointing to a real directory
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^[^/]+/?$ fr%{REQUEST_URI} [L]

I don't know how you try include assets in you source code, So you can try make this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)/(img|css|js|fonts)/(.*)$ assets/$2/$3 [QSA,L]
RewriteRule ^(.*)/(fr|it|en|de)/(.*)$ $2/index.php?area=$3 [QSA,L]
RewriteRule ^.htaccess$ - [F]

Related

htaccess - Show cached file if present in cache directory, if not, show the original file

I've the same requirement as this question has - htaccess->show file if in cache directory, if not continue but the only difference is, the solution provided to that question is not working in my case.
The requirement:
Display the cached file from the cached folder if it exists.
Eg:
domain.com/abc/def/the_file (requested page)
domain.com/cache/abc/def/the_file.html (the cached version)
Here, the_file is actually an existing PHP file (the_file.php), which is being processed by the rewrite rules when the URL contains the_file.
And I guess that the solution is not working due to current set of htaccess rules that I already have in my htaccess file, though, I'm unable to figure out what's causing it to not work.
Any help would be much appreciated.
The htaccess file:
RewriteEngine On
# RewriteBase equivalent - Production
RewriteCond %{HTTP_HOST} !^localhost$
RewriteRule . - [E=REWRITEBASE:/]
# RewriteBase equivalent - Development
RewriteCond %{HTTP_HOST} ^localhost$
RewriteRule . - [E=REWRITEBASE:/projects/projectname/]
# Block Directory Listing
Options -Indexes
# 404 Page
ErrorDocument 404 /inc/404.php
# Redirecting index.php to base directory
RewriteCond %{THE_REQUEST} ^.*/index\.php
RewriteRule ^(.*)index.php$ /$1 [R=301,L,QSA]
# Removing trailing slash from URL
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ %{ENV:REWRITEBASE}$1 [R=301]
# To externally redirect /dir/abc.php to /dir/abc
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s/+(.+?)\.php[\s?] [NC]
RewriteRule ^ /%1 [R=301,L,NE]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^([^\.]+)/?$ $1.php [QSA,NC,L]
### Serving Cached Files if Exists
# check if the file exists in cache dir
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}cache/$1.html -f
RewriteRule ^(.+?)/?$ /cache/$1.html [L]
Note: I'm testing this thing on the production version, as the %{DOCUMENT_ROOT} variable value will differ on the XAMPP localhost.

Unexpected behavior with custom .htaccess rewrite rules

So I have been working on some rewrite rules which aren't working as expected. These are some sample requests I am trying to rewrite:
/ -> /index.php?page=Home
/Home -> /index.php?page=Home
/Teaching/Foo -> /index.php?page=Teaching&id=Foo
/Teaching/Bar -> /index.php?page=Teaching&id=Bar
/Download/ue8 -> /index.php?action=Download&id=ue8
/Download/24a -> /index.php?action=Download&id=24a
(default) -> /index.php?page=Home
** OR ALTERNATIVELY **
(default) -> /index.php?page=FileNotFound
(and maybe rewrite the visible URL to /FileNotFound)
I mainly want to hide as much of the urls as possible and prevent both directory listing and direct access to my files located in specific folders and only allow access to downloadable files via /Download/FileId while having my usual pages for different lectures accesible via /Teaching/SomeLecture.
So far I have been using this snippet for the /Home and /Teaching stuff:
RewriteEngine On
RewriteBase /
# Redirect Trailing Slashes
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Handle Front Controller
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^Teaching/([A-Za-z0-9]*)$ index.php?page=Teaching&id=$1 [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^Home$ index.php?page=Home [L]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s(.*)/index\.php [NC]
RewriteRule ^ %1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,R=301]
I am not completely sure about all of these directives and I noticed, that there are currently some flaws to it.
Trying to access a non-existant file, e.g. /Files/Bad/Path.pdf, forwards the user to /?page=Home which should be redirected either to /, /Home or display the contents of /index.php?page=FileNotFound without changing the URL at all or redirecting to /FileNotFound depending on the rule of (default). I am not really sure which solution might be the most suitable in this scenario.
Trying to access some folders which do exist results in an infinite redirection loop while folders which do not exist apparently redirect to /. In both cases it feels right to redirect to /FileNotFound I suppose?
Could you please work out a set of rules that might suit my needs in this case?
You have many redundant directives in your .htaccess. Replace all of your .htaccess with this:
# Turn off mod_spelling
<IfModule mod_speling.c>
CheckSpelling off
CheckCaseOnly off
</IfModule>
Options -MultiViews
RewriteEngine On
RewriteBase /
# block direct access to file and directories in these directories
RewriteCond %{REQUEST_URI} !\.(?:jpe?g|gif|bmp|png|tiff|css|js)$ [NC]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(Templates|Files) - [NC,F]
# remove index.php
RewriteCond %{THE_REQUEST} /index\.php [NC]
RewriteRule ^(.*?)index\.php$ /$1 [L,R=301,NC,NE]
# Redirect Trailing Slashes
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Handle Front Controller
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(Download|Teaching)/([\w-]+)/?$ index.php?page=$1&id=$2 [L,QSA,NC]
RewriteRule ^(Home)?/?$ index.php?page=Home [L,NC,QSA]
Make sure to clear your browser cache before testing this.

Combine "folder as GET parameter" and "subdomain as GET parameter"

I have a site that rewrites path after domain to a page GET parameter.
##REWRITING DIRECTORIES TO GET PARAMETERS
RewriteBase /
#Ignore all real directories (do not rewrite them)
RewriteCond %{REQUEST_FILENAME} !-d
#Also do not rewrite real files
RewriteCond %{REQUEST_FILENAME} !-f
#For everything else, index.php should fetch the proper content
RewriteRule ^([^/]*)$ index.php?page=$1 [QSA,L]
##This means:
#example.com/help
#~becomes~
#example.com?page=help
The site uses multiple languages and so far, I've been using cookies to set and remember the language for the user. While the convenience for the user is disputable, this is definitely not convenient for the SEO.
I need to rewrite [a-z]{2}\.mydomain\.xx to index.php?lang=$1 so that user will be always on en.domain.com for example. There are examples to do this, however I'm still confused about how the rewrite engine works and I don't know how should I combine my new rules with the old ones:
##Language rewrite
#Copypasted. Didn't understand
RewriteCond %{HTTP_HOST} ^([a-z]{1,2})\.domain\.xx
RewriteRule ([a-z]{1,2})\.domain\.xx index.php?lang=$1 [QSA,L]
How can I get en.domain.com/help turn in index.php?page=help&lang=en?
How can I get en.domain.com/help turn in index.php?page=help&lang=en
You can use:
RewriteEngine On
RewriteBase /
#Ignore all real directories (do not rewrite them)
RewriteCond %{REQUEST_FILENAME} !-d
#Also do not rewrite real files
RewriteCond %{REQUEST_FILENAME} !-f
#For everything else, index.php should fetch the proper content
RewriteCond %{HTTP_HOST} ^([a-z]{1,2})\.domain\.xx$ [NC]
RewriteRule ^([^/]+)/?$ index.php?lang=%1&page=$2 [QSA,L]
Reference: Apache mod_rewrite Introduction

Rewrite rule with 2 variables with "/" separation

I have an .htaccess file located in a folder "/mixtapes/" I am trying to get the URL mydomain.com/music/downloads/mixtapes/this-title/id to execute mydomain.com/music/downloads/mixtapes/item.php?title=variable1&id=variable2
I currently have the below way somewhat working but it only uses the id and I need both variables (../mixtapes/title/id)separated by "/" and for some reason with the below code the index page inside "/mixtapes/" does not work.I am stumped! I am somewhat new to this and any help is greatly appreciated!
BTW on my index page the passing url to item.php page is rewritten to <a href="title/id">I just cant seem to get it to properly execute item.php?title=a&id=b with the format mixtapes/title/id
Current htaccess file located in "/mixtapes/"
# turn mod_rewrite engine on
RewriteEngine On
# rewrite all physical existing file or folder
RewriteCond %{REQUEST_FILENAME} !-f [OR]
RewriteCond %{REQUEST_FILENAME} !-d
# allow things that are certainly necessary
RewriteCond %{REQUEST_URI} "/css/" [OR]
RewriteCond %{REQUEST_URI} "/images/" [OR]
RewriteCond %{REQUEST_URI} "/images/" [OR]
RewriteCond %{REQUEST_URI} "/javascript/"
# rewrite rules
RewriteRule ^mixtapes/item.php(.*) - [L]
RewriteRule ^(.*) item.php?id=$1 [QSA]
The comments in your .htaccess actually state wrong
# turn mod_rewrite engine on
RewriteEngine On
# if requested URL is NOT a existing file
RewriteCond %{REQUEST_FILENAME} !-f [OR]
# or if requested URL is NOT a directory
RewriteCond %{REQUEST_FILENAME} !-d
# CSS, images, JavaScript are files and we will never pass this point when those are requested, next rules can be skipped
# rewrite rules
# rewrite /mixtapes/title/id to item.php?title=title&id=id
Rewrite ^mixtapes/([^/]+)/([0-9]+) item.php?title=$1&id=$2 [L]
# catch all other requests and handle them ( optional, default would be 404 if not a physically existing file )
Rewrite (.*) index.php [L,QSA]
I've assumed that your id is a numeric value.
Be aware with the use of the title in php. Don't output this directly but you can use it to verify your URL and redirect wrong title/id combos

Mod Rewrite ignoring -d

After a day or two, I am still fighting with Mod Rewrite. It seems that 60% of my development time is spent battling the server.
My current URL structure is such that all http://example.com/xyz and http://example.com/xyz/abc should be handled by index.php. The problem is I have a http://example.com/admin/ section, which is a real directory which I need to be accessible via HTTP request (it's the CMS directory)
When I try to browse to the CMS http://example.com/admin/, It changes my URL to http://example.com/admin/?n=admin and returns a 404. My index.php is receiving n=admin as it's argument.
What I cant understand is why these two conditions are being ignored:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !^admin(?:\/)?$
And why I'm getting that redirect to http://example.com/admin/?n=admin (Rather than just stopping at http://example.com/admin/.
Options +FollowSymlinks
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !^admin(?:\/)?$
# allow access to certain subdirectories.
RewriteRule ^admin(?:\/)?$ /admin/ [L,NC]
# redirect all old URLs to new pages (or 404 sitemap page if no analog?).
RewriteRule ^company/about(?:\/)?$ /company [R=301,L,NC]
# catch any others and try to serve them right
RewriteRule ^/?(.+).html$ /$1 [R=301,L]
RewriteRule ^/?([0-9]+)(?:\/)?$ /index.php?p=$1 [L]
RewriteRule ^/?([-a-zA-Z0-9_+]+)(?:\/)?$ /index.php?n=$1 [L]
RewriteRule ^/?([-a-zA-Z0-9_+]+)/([-a-zA-Z0-9_+]+)(?:\/)?$ /index.php?n=$2 [L]
Can anyone offer any ideas or point out any flaws in the .htaccess?
Each RewriteCond only applies the the next RewriteRule.
so only your first Rule has a condition.
Your options are to repeat the rules, or make then final and exiting (cannot remember flag for this off my head)
Try this rule before your other rules:
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
This will end the rewrite process if the requested path can be mapped to an existing directory. The same applies when using -f instead.