.htaccess rule - check if the request is for a folder or file? - apache

I need to check the requested URL, and serve different options depending on whether the request is for a file or a directory.
My URLs would look like:
http://www.example.com/Services/Service-1 (directory, serve /pages/Services/Service1/index.php)
http://www.example.com/Services/Service-1/Feature-1/Sub-Feature (an actual file, serve /pages/Services/Service-1/Feature-1/Sub-Feature.php)
Because of my lack of understanding of .htaccess (would this need a RewriteCondition?), I am currently stuck enumerating out each and every folder of my directory structure as follows:
RewriteRule ^Services/Service-1/(.*)$ /pages/Services/Service-1/$1.php
RewriteRule ^Services/Service-1 /pages/Services/Service-1/index.php
RewriteRule ^Services/Service-2/(.*)$ /pages/Services/Service-2/$1.php
RewriteRule ^Services/Service-2 /pages/Services/Service-2/index.php
RewriteRule ^Services/(.*)$ /pages/Services/$1.php
RewriteRule ^Services /pages/Services/index.php
RewriteRule ^Testimonials/(.*)$ /pages/Testimonials/$1.php
RewriteRule ^Testimonials /pages/Testimonials/index.php
Needless to say, this is a real pain - any time I add folders of content, I have to mess with .htaccess.
I know there must be a better way, but my google and stackoverflow searches haven't turned up anything that works when I try it.

you guessed it right, a rewriteCond can be used to verify if the requested uri is a file or a directory:
# f for a file, d for a directory
RewriteCond %{REQUEST_FILENAME} -f
you .htaccess would be:
Options -MultiViews
RewriteEngine ON
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule (.+) /$1/index.php [QSA,L]
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule (.+) /$1.php [QSA,L]
EDIT:
if your files reside in the page sub directory , you have to use the following code:
Options -MultiViews
RewriteEngine ON
RewriteCond %{DOCUMENT_ROOT}/pages/$1 -d
RewriteRule (.+) /pages/$1/index.php [QSA,L]
RewriteCond %{DOCUMENT_ROOT}/pages/$1.php -f
RewriteRule (.+) /pages/$1.php [QSA,L]

By far the easiest if mod negotiation is enabled (it usually is):
Options MultiViews
MultiviewsMatch Any
DirectoryIndex index.php
Won't force the .php though, if you have somefile.html as well as somefile.php the .html file is usually selected.

Related

Multiple RewriteConds / RewriteRules depending on file existence of single rule targets

I am currently working on a static website where I need the last URI-element being passed along as query-parameter on some pages.
All my static html pages are inside the /pages/**/* directory, where the paths directly correlate to the desired paths on the final website. Meaning /products/kitchen would require /pages/products/kitchen.html to be shown. If, in this example, the file /pages/products/kitchen.html would not exist, I would like to try /pages/products.html instead and pass on kitchen as query argument named id (?id=kitchen). There are static assets in an /assets directory, so files that exist should not be rewritten to the /pages directory at all. If there is no requested path, I would like to redirect to the default home-page. The probe.json rule is for liveliness-checks in a kubernetes cluster, I just kept it in there because I can not be 100% sure that it is not the culprit.
I tried to chain multiple RewriteConds and RewriteRules together in an if > else if > else if > else way, but that seems to be failing at some point. Unfortunately, those errors are very tricky to debug because there is no indication of what is happening besides the browsers 404-error. Currently, only the rule for existing files and for the default page is working correctly. Any help on what I am doing wrong would be appreciated!
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .? - [L]
<files probe.json>
order allow,deny
allow from all
</files>
RewriteCond pages/$1.html -f
RewriteRule ^(.*)$ pages/$1.html [NC,L]
RewriteCond pages/$1.html -f
RewriteRule ^(.*(?=[/]))/(.*)$ pages/$1.html?id=$2 [NC,L]
RewriteRule ^$ pages/home.html [L]
I found the solution for this problem.
I had to use absolute paths.
I had to add %{DOCUMENT_ROOT} infront of the condition.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .? - [L]
<files probe.json>
order allow,deny
allow from all
</files>
RewriteCond %{DOCUMENT_ROOT}/pages/$1.html -f
RewriteRule ^(.*)$ /pages/$1.html [NC,L]
RewriteCond %{DOCUMENT_ROOT}/pages/$1.html -f
RewriteRule ^(.*(?=[/]))/(.*)$ /pages/$1.html?id=$2 [NC,L]
RewriteRule ^$ /pages/home.html [L]

Mod rewrite to remove subdirectory and file extension without breaking DirectoryIndex

I have all site pages in a subdirectory like this...
http://www.example.com/pages/myfile.php
I want the URL to look like this...
http://www.example.com/myfile
Where both the subdirectory called pages and the .php file extension are removed from the URL.
My latest (partial) attempt...
Options All -Indexes +FollowSymLinks
DirectoryIndex index.php
RewriteEngine On
RewriteBase /
RewriteCond %{DOCUMENT_ROOT}/pages%{REQUEST_URI}\.php -f [OR]
RewriteCond %{DOCUMENT_ROOT}/pages%{REQUEST_URI} -d
RewriteRule ^(.*)$ /pages/$1.php [NC,L]
However, this totally breaks DirectoryIndex. When I go to http://www.example.com/ or http://www.example.com/foo/, I get a 404 error instead of defaulting to index.php as defined by DirectoryIndex.
Apparently, it treats everything as a file name instead of recognizing the lack of a file name (directory) and attempting to use index.php.
I tried incorporating this solution into mine, it fixed the DirectoryIndex issue, but it broke everything else.
Is there a solution? Please include a detailed explanation within your answer so I can learn where/how I was going wrong.
Try this in root .htaccess:
Options All -Indexes +FollowSymLinks
DirectoryIndex index.php
RewriteEngine On
RewriteBase /
# add a trailing slash if pages/<uri> is a directory
RewriteCond %{DOCUMENT_ROOT}/pages/$1 -d
RewriteRule ^(.*?[^/])$ %{REQUEST_URI}/ [L,R=302]
RewriteRule ^/?$ pages/index.php [L]
# skip all files and directories from rewrite rules below
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
# if corresponding .php file exists in pages/ directory
RewriteCond %{DOCUMENT_ROOT}/pages/$1\.php -f
RewriteRule ^(.+?)/?$ pages/$1.php [L]
# route all requests to pages/
RewriteRule ^((?!pages/).*)$ pages/$1 [L,NC]

php-fpm - How to execute certain symlinks as PHP scripts

I am running Apache 2.2 with FastCGI and php-fpm. I am trying to duplicate the following logic:
<FilesMatch "^(admin|api|app)?(_dev)?$">
#ForceType application/x-httpd-php
SetHandler php-fcgi
</FilesMatch>
Which allows me to symlink admin.php as admin, so I can remove the .php extension. It seems the only way to do this with php-fpm is to set the security.limit_extension of the www.conf file to empty, however, as the comments indicate, this is a pretty big security hole, in that php code can now be executed from within any file, regardless of extension.
What would be the preferred way to accomplish the above, but still maintain some semblance of security?
Looks like the best solution so far is to manually add the known symlinks to the list (located in /etc/php-fpm.d/www.conf):
security.limit_extension php admin admin_dev api api_dev app app_dev
Not sure if security.limit_extension directive can even take a regex, doesn't look like it, so this is about as good as it gets. As mentioned in the OP, you will still have to maintain the filesmatch directive in the vhost config as well:
<FilesMatch "^(admin|api|app)?(_dev)?$">
SetHandler php-fcgi
</FilesMatch>
-- Update --
Per the comments by tftd, adding current rewrite directive:
RewriteBase /
# we skip all files with .something
RewriteCond %{REQUEST_URI} \..+$
RewriteCond %{REQUEST_URI} !\.html$
RewriteRule .* - [L]
# we check if the .html version is here (caching)
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
# no, so we redirect to our front web controller
RewriteRule ^(.*)$ index.php [QSA,L]
#Mike, based on your updated answer, something similar to this .htaccess file should be able to handle what you're trying to do:
# Enable the rewrite engine
RewriteEngine on
# Set the rewrite base path (i.e. if this .htaccess will be running at root context "/" or a subdir "/path")
RewriteBase /
# If the file exists, process as usual.
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .* - [NC,L]
# If the dir exists, process as usual (if you don't need this, just comment/remove the next two lines).
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [NC,L]
# If (requested_file_name).html exists, rewrite to that file instead.
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html [QSA,L]
# If (requested file name).php exists, rewrite to that file instead.
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.html [QSA,L]
# If none of the above rules were triggered, fallback to index.php.
RewriteRule ^(.*)$ index.php [QSA,L]
With a bit of tweaking this should be able to do the job without the need of having to dive into httpd.conf and the <VirtualHost> nor <FilesMatch> directives. Hope this helps.

Url renaming using .htaccess file

Most are for redirections and removing file extensions or for dynamic urls.
The problem being I can't get a simple static url to rename
Options +FollowSymlinks
RewriteEngine On
RewriteRule ^fileThathasalongname file.html
What I currently have 'mysite.co.uk/fileThathasalongname.html'
What I want is 'mysite.co.uk/file/' while file = file.html
using:
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteRule ^FileIWantToChange.html FriendlyNamed.html
Using this gives me the error multiple Choices
+++++++++++++++++Edit+++++++++++++++++++++++++
Thought i'd add my final version for people to look at, it may help, the anwser below is correct.
Options +FollowSymLinks -MultiViews
DirectorySlash Off
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME}/ -d
RewriteCond %{SCRIPT_FILENAME}.html !-f
RewriteRule [^/]$ %{REQUEST_URI}/ [R=301,L]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(.+)\.html$ /$1 [R=301,L]
RewriteRule ^FriendlyNamed.html FileIWantToChange.html [L]
RewriteCond %{SCRIPT_FILENAME}.html -f
RewriteRule [^/]$ %{REQUEST_URI}.html [QSA,L]
RewriteCond %{HTTP_HOST} ^www.mysire.co.uk [NC]
RewriteRule ^(.*)$ http://mysite.co.uk/$1 [L,R=301]
all works a charm!
I see multiple issues going on. Firstly the regular expression is matched against the friendly URL the user types in. So you need to swap your friendly url and file path with each other. The friendly or "fake" name goes on the left while the url to redirect to silently goes on the right. Also make sure you've set the directory base to /. Finally it's good to add an [L] to enforce it to be the last rule in case anything in the same file tries to rewrite the path. Due note that other htaccess files lower down, depending on the files location, will also be checked even when enforcing the rule has the last rule. Also junk the options part completely. Give this a try:
RewriteBase /
RewriteEngine On
RewriteRule ^FriendlyNamed.html FileIWantToChange.html [L]
RewriteRule ^fileThathasalongname.html file.html

How can I use mod_rewrite on current directory only?

Currently I have these rules in my .htaccess file:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*).css style.php?u=$1 [QSA]
RewriteRule ^(.*).xml rss.php?u=$1 [QSA]
</IfModule>
This will rewrite the following URLs:
http://domain.com/user.css
http://domain.com/user.xml
But when I'm trying to grab a file from a subdirectory: http://domain.com/css/style.css it gets rewritten as well.
My goal is rewrite only for current directory and avoid sub-directories, since all real CSS files on sub-directories will be rewritten.
How I can avoid this?
You need to make your pattern more restrictive: this ^(.*).css will match ANYTHING with .css in it while this pattern ^([^/]+)\.css$ will be restricted to something.css (styles\something.css will not match it).
<IfModule mod_rewrite.c>
RewriteEngine On
# do not do anything to real files or folders
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .+ - [L]
RewriteRule ^([^/]+)\.css$ style.php?u=$1 [QSA,L]
RewriteRule ^([^/]+)\.xml$ rss.php?u=$1 [QSA,L]
</IfModule>
The easiest way is to tell mod_rewrite to avoid rewriting if the file is a real file:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*).css style.php?u=$1 [L,QSA]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*).xml rss.php?u=$1 [L,QSA]
I've added a [L] tag (end) because once a rule is applied you certainly doesn't need the next rule to be checked.
Now if the files really exists but you really want to handle them with a php script if no 'css' subdirectory is present on the url... let's try that:
<Location "/css">
RewriteEngine off
</Location>