Ignore directory if matching filename exists - .htaccess

I am currently using:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.+)/$ $1.html [L]
..To make pretty URLs for my .html files.
Assuming a directory structure like this:
/services/service-one.html
/services/service-two.html
/services.html
/something-else.html
service-one.html, service-two.html & something-else.html all work properly, but I can't get services.html to load using the /services URL because there is a directory matching the name.
Is there a way that this behaviour can be changed so that RewriteCond %{REQUEST_FILENAME}.html -f works regardless of the URL matching a directory? Or maybe another way of achieving the same result?
Thanks!
Edit
After removing my .htaccess and finding those non-.html links still work, I had a look in my VirtualHost and wonder if it is something in there causing it? I can see that MultiViews is what's giving me the URL rewrite behaviour that I wanted to achieve in the .htaccess
<VirtualHost *:80>
DocumentRoot "/mysite"
ServerName testsite.com
<Directory "/mysite">
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
# Other directives here
</VirtualHost>
Edit 2
Behaviour remains the same with no MultiViews, and using Anonymous's .htaccess

You have two options:
The reason this rule is not working is that you require a trailing slash to add the .html extension. You can just remove that requirement in the rewrite regex:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.+)$ $1.html [L]
The better way to do this is to create a file named index.html in the services directory. Then, when you request the directory without a filename, that file is automatically used.
For the first option to work, you would also need to change the default behavior. You can use this line to prevent adding a trailing slash to directories:
DirectorySlash Off

Related

500 internal error with RewriteEngine on .htaccess on localhost with wamp

I'm having a problem with a script. It doen't works with a htaccess file that is needed to work. Here's what the htaccess contains. I'm trying to install it on a wamp localhost. The code is:
#AddType x-mapp-php5 .php
#AddHandler x-mapp-php5 .php
RewriteEngine ON
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php/$0 [PT,L]
#RewriteRule ^(.*)$ /index.php?q=$1 [L,QSA]
Options All -Indexes
If I remove this it works:
RewriteEngine ON
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php/$0 [PT,L]
#RewriteRule ^(.*)$ /index.php?q=$1 [L,QSA]
But this way the script loads but every page show error 404. Is there a way to resolve this problem??
It looks like you don't have the rewrite modules loaded. Find your httpd.conf file and make sure this line (or something similar) is uncommented:
LoadModule rewrite_module modules/mod_rewrite.so
Check that you have the the apache rewrite module loaded.
go to wamp_manager -> apache -> modules and look for rewrite_module in the list.
If it does not have a TICK beside it click it. Apache will be bounced ( stop, start ). Try again.
The rewite engine will not work without the required module loaded.
I had the same problem. To un-comment the line, remove the # in front of the line
LoadModule rewrite_module modules/mod_rewrite.so
Worked for me in Wamp.
Directory of the httpd.conf file: C:\wamp\bin\apache\apache2.4.9\conf
This is one solution that solved the issue for me. Rewrite module was always enabled, used in IfModule rewrite_module, permissions were granted and .htaccess contents were fine, yet it still was 500 error trying to use rewrite module.
In httpd.conf by default:
This is a source of a 500 error if you try to use rewrite in .htaccess in some sub directory.
`
# Deny access to the entirety of your server's filesystem. You must
# explicitly permit access to web content directories in other
# <Directory> blocks below.
#
<Directory />
AllowOverride none
Require all denied
</Directory>
`
So one may want to use .htaccess with rewrite module in a specific directory. You would add a <directory> block for that directory. If one copies and pastes the directory block, you need to make sure the intent of the block you copy is correct for the directory you want to apply it to.
So for my intent this block, causes a 403 error, but does get rid of the 500 error.
<Directory "c:/Apache24/htdocs/store">
AllowOverride All
Options None
Require all granted
</Directory>
Changing to this solved the issue:
<Directory "c:/Apache24/htdocs/store">
AllowOverride All
Require all granted
</Directory>
I suppose this is why the issue is commonly seen, but rarely solved in these threads. If I simply copied a different block, typed my own block, or had any understanding of what I was doing, this wouldn't have been an issue.
I can't say this solves everybody's issue, but I hate when people solve-and-run w/o enlightening the rest of us. So for those that did my mistake, this is the answer.

htaccess multi language site with sub directories, and default 301

I am having some issues setting up my htaccess to allow multiple languages utilising the sub directory method eg:
http://www.domain.com/en/
http://www.domain.com/sw/
http://www.domain.com/ie/
Also to complicate things, the project isn't currently live, its on a dev server. For example, I am currently accessing the project at:
http://dev.domain.com/devname/projectname/
And I want the above to automatically 301 redirect to:
http://dev.domain.com/devname/projectname/en/
Here is my htaccess:
Options +FollowSymLinks -MultiViews
RewriteEngine on
# ----------------------------------------------------------------------
# MULTI LANGUAGE SUB DIRECTORY
# ----------------------------------------------------------------------
RewriteCond %{REQUEST_URI} !^/(en|sw)/
RewriteRule ^(.*)$ en/$1 [R=301,L]
# ----------------------------------------------------------------------
# Rewrite rules
# ----------------------------------------------------------------------
## CASE STUDIES ##
RewriteRule ^casestudies/([^/\.]+).html$ index.php?controller=contents&method=viewCasestudy&link=$1 [L,QSA]
## PRODUCTS ##
RewriteRule ^products/([^/\.]+).html$ index.php?controller=contents&method=viewProduct&link=$1 [L,QSA]
RewriteRule ^([a-z{2}]+)(/)?$ index.php?controller=contents&method=viewHome&lang=$1 [L,QSA] # Default load
RewriteRule ^(/)?$ index.php?controller=contents&method=viewHome [L,QSA] # Default load
The above will actually redirect to:
http://dev.domain.com/home/webserver_dir/devname/projectname/en/
..and if I use RewriteBase it seems to just goto...
http://dev.domain.com/en/
So my question: How do I get the language URLs working correctly relative to the directory its in on my dev server, and then ideally will work when it goes live without any environment specific rules.
Bonus question: Do I need to add the ([a-z{2}]+) bit in front of all my subsequent rewrite rules or can I have a catch all that will effect all further rules?
EDIT -----------------------------
I have reduced it down to the following as suggested...
Options +FollowSymLinks -MultiViews
RewriteEngine on
RewriteBase /devname/projectname/
RewriteCond %{REQUEST_URI} !^/(en|sw)(/|$) [NC]
RewriteRule ^(.*)$ en/$1 [R=301,L]
RewriteRule ^([a-z]{2})/?$ index.php?controller=contents&method=viewHome&lang=$1 [NC,L,QSA] # Default load
... but now its redirecting to http://dev.domain.com/devname/projectname/en/en/en/en/en/en/en/en/en/en/en/en/en/en/en/en/en/en/en/en/en/, any ideas?
Have you tried the answer in the following link? It should do what you're trying to achieve.
Endless Redirect Loop by htaccess rules multi language
RewriteEngine On
RewriteBase /
# empty url -> redirect to en/
RewriteCond %{QUERY_STRING} !lang=(en|de)
RewriteRule ^$ en/ [R=301,L]
# url is ONLY '/en' or '/de' -> redirect to /en/ or /de/ (adding slash)
RewriteRule ^(en|de)$ $1/ [R=301,L]
# now all urls have en/ de/ -> parse them
RewriteRule ^(en|de)/(.*)$ $2?lang=$1&%{query_STRING} [L]
If .htaccess must not change
Change your <VirtualHost> configuration for your DEV server project as
<VirtualHost *:80>
ServerName dev.domain.com
ServerAlias project.domain.com
DocumentRoot "/home/webserver_dir/devname/projectname"
</VirtualHost>
These changes would typically go in your httpd-vhosts.conf file. Your .htaccess files would now have
RewriteBase /
to mark root as your base directory for both your development and live servers.
If you're trying to version your projects or test multiple projects on the same dev host, then you would have to incorporate the naming scheme into the domain names instead of the URL path. For example,
<VirtualHost *:80>
ServerName dev1.domain.com
ServerAlias project1.domain.com
DocumentRoot "/home/webserver_dir/dev1/project1"
</VirtualHost>
<VirtualHost *:80>
ServerName dev2.domain.com
ServerAlias project2.domain.com
DocumentRoot "/home/webserver_dir/dev2/project2"
</VirtualHost>
The bottom line is that you can not have the same .htaccess file rules working untouched with different deployment directories unless you resort to mod-rewrite way of if-else mumbo jumbo which would just be added clutter once you've gone live.
For the rules to work transparently, Apache must only see and apply the rules on what's going live (the content that comes after /devX/projectX/ directories) which is what shifting the DocumentRoot does here for us.
If minimal mods to .htaccess are okay
Not everyone has access to Apache's .conf files. Certain hosts out-rightly reject requests to modify them. Which is why, if they have at least kept mod-rewrite enabled, a lot of website's settings can be tinkered with. One of them is to use RewriteBase to handle the different deployment directories.
So, if you keep RewriteBase / on live but change it to RewriteBase /devX/projectX/ for development, most of your RewriteRules should work as is. So, /devname/projectname/ should correctly redirect to /devname/projectname/en/.
Your use of ([a-z{2}]+) is incorrect. You probably meant ([a-z]{2}) to capture exactly two letters. If you meant to capture two or more, it would become ([a-z]{2,}). So, your default load rewrite would become
RewriteRule ^([a-z]{2})/?$ index.php?controller=contents&method=viewHome&lang=$1 [NC,L,QSA] # Default load
You're correct to assume that you would need this regex for all subsequent rules or they would fail to match. So, your RewriteRule for casestudies won't work. A simpler way to not care about the language prefix is to drop the ^ start of URL path anchor as
RewriteRule /casestudies/([^/\.]+).html$ index.php?controller=contents&method=viewCasestudy&link=$1 [NC,L,QSA]
RewriteRule /products/([^/\.]+).html$ index.php?controller=contents&method=viewProduct&link=$1 [NC,L,QSA]
Your last RewriteRule matching ^(/)?$ isn't required because you're already doing a 301 redirect for all URLs with no language directory prefix to /en/$1 above, which should ideally be
RewriteCond %{REQUEST_URI} !^/(en|sw)(/|$) [NC]
RewriteRule ^(.*)$ en/$1 [R=301,L]
Otherwise, /en would get redirected as well to /en/en.

Can we use httpd.conf instead of .htaccess for clean URL?

I have read that setting changes in httpd.conf are much better as compared to making changes in .htaccess as the later one is parsed at Runtime while .conf is parsed at the time of starting Apache. So, is it possible to have all the .htaccess functionality in httpd.conf or there are a few things that have to done in .htaccess only.
Also, can you suggest how to debug clean URL issues?
Basically, I am not able to get the clean URL working. I can access the show.php file but am not able to access the GET variables. Here is the .conf settings.
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\.example\.com [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^/show/([^/\.]+)/([^/\.]+) http://www.example.com/show.php?id=$1&img=$2 [L,R]
I have also added
Options Indexes MultiViews FollowSymlinks
AllowOverride All
in <Directory "/var/www/html">
Thanks.
As stated in the Apache manual on .htaccess, anything you can put in an .htaccess file can be put inside a <Directory> block within httpd.conf for better efficiency.
As for your issues with rewriting the URL, your first RewriteCond appears to only be matching URLs that aren't www.example.com (the ! in front is a negation modifier). Remove the ! and see if that fixes the issue. (You probably should remove the second RewriteCond as well, until you have the first one matching; otherwise, debugging problems will be more difficult.)

Auto-append dummy folder name to end of base URL

Take a look at this sample vhost snippet:
<VirtualHost *:80>
DocumentRoot /web/content
ServerName me.example.com
</VirtualHost>
Say /web/content contains 2 files, index.html and page.html, and a subdirectory colors containing yellow.html. index.html contains links like href="/page.html" and href="/colors/yellow.html", using / to refer to the web root.
Is there a way to internally use me.example.com/test/ as the root of the site? In other words, when a user goes to http://me.example.com/test/, I want to fetch /web/content/index.html. So, the /test/ in the URL would essentially be a dummy "folder".
Basically, I'd like me.example.com/test to function exactly how a subdomain (say, test.me.example.com) would. Something like setting ServerName me.example.com/test (but I know that doesn't work).
I know I could simply make a REAL folder /web/content/test and put everything in there, but that would break my links beginning with a /, as they would still refer to /web/content as the web root. The same problem arises when adding a directive like Alias /test /web/content.
Do I have any options here? Maybe using RewriteBase somehow? (I've tried a few things with no luck.)
Try putting this in the htaccess file in your document root
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/test/(.*)$
RewriteCond %{DOCUMENT_ROOT}/%1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/%1 -d
RewriteRule ^ /%1 [L]
This maps the /test/ URI path to the document root. If you want to say, map it to the colors folder, you'd do:
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/test/(.*)$
RewriteCond %{DOCUMENT_ROOT}/colors/%1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/colors/%1 -d
RewriteRule ^ /colors/%1 [L]

Remove file extensions using htaccess in subdirectories

I'm trying to remove file extensions with htaccess, so www.mysite.com/file.php becomes www.mysite.com/file.
I'm using the following code in the .htaccess file:
Options +FollowSymLinks
Options +Indexes
RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^([^\.]+)$ $1.php [NC,L]
So far, so good. Where it falls down is in subfolders: www.mysite.com/subfolder/file.php
becomes www.mysite.com/file.
I've tried uploading another .htaccess file in the subfolder, but it still does the same. It feels like it should be really simple, but I'm struggling...can anyone help me out? Thanks!
Edit Sorry folks, should have said - the file is in a subfolder like so:
www.mysite.com/folder/subfolder/file.php
The .htaccess file is in /folder, the URL changes to this format:
www.mysite.com/subfolder/file
Apologies for misleading.
This is the rule you'll need to hide .php extension. This goes into your .htaccess in the DOCUMENT_ROOT directory:
Options +FollowSymLinks -MultiViews
# Turn mod_rewrite on
RewriteEngine On
# To internally redirect /dir/foo to /dir/foo.php
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^ %{REQUEST_URI}.php [L]
You shouldn't be using rewrite rules for this. Apache has built-in option explicitly for doing what you're trying to do, called MultiViews, and that's what you should be using:
The effect of MultiViews is as follows: if the server receives a request for /some/dir/foo, if /some/dir has MultiViews enabled, and /some/dir/foo does not exist, then the server reads the directory looking for files named foo.*, and effectively fakes up a type map which names all those files, assigning them the same media types and content-encodings it would have if the client had asked for one of them by name. It then chooses the best match to the client's requirements.
Just add Options MultiViews to your .htaccess, and make sure that AllowOverride is properly configured.
A guess in the wild
Try
RewriteRule ^(.+)$ $1.php [NC,L]

Resources