Apache rewrite condition to fall back when css doesn't exist - apache

I need a rule in Apache that redirects not-found CSS files to another location based on their names in another folder. Like this:
Request: localhost/css/nonexistent.css
Response: localhost/css/g/nonexistent.css
If the CSS exists, just serve it like normal:
Request: localhost/css/existent.css
Response: localhost/css/existent.css
My project is on CakePHP which comes with the following rules by default:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,L]
I mention it because whatever the new rule is, it should not break Cake's rules.
Thanks for the help.
Edit: Forgot to mention that the css/g/ file is an alias for a script (inside the Cake MVC stack) that generates the new css file and echoes it. Answers so far seem to do the redirection fine, but then it doesn't find the css/g/file.css either because it really doesn't exist.

Here is a rewrite rule adapted from this SO question and the Apache mod_rewrite docs.
The gist is: If the request is for a path that starts with /css/,get the filesystem path of the requested file and check if it exists. If id doesn't, rewrite the URL for your deeper directory. This should be placed before the rules you posted in your question.
RewriteCond %{REQUEST_URI} ^/css/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/css/(.*) /css/g/$1

You can try first checking if the /css/g/ css file exists:
# Make sure it doesn't exist
RewriteCond %{REQUEST_FILENAME} !-f
# Make sure this is a request for a css file:
RewriteCond %{REQUEST_URI} ^/css/(.*)\.css$
# See if the /g/ version exists
RewriteCond %{DOCUMENT_ROOT}/css/g/%1.css -f
# rewrite if all conditions satisfied
RewriteRule ^css/(.*)$ /css/g/$1 [L]
The %1 in the 3rd condition backreferences the filename (sans .css extension) matched in the previous RewriteCond.
EDIT:
If the file css file is actually generated, then skip the checking of /g/ version and just pass it to the controller:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} ^/css/(.*)\.css$
RewriteRule ^css/(.*)$ index.php/css/g/$1 [L]

Related

Redirecting all urls, including no path, to a file in subdirectory

I have checked a large amount of existing answers regarding .htaccess redirects. However none of them have helped me.
What I want to accomplish is redirecting all request urls to /api/init.php. However I've only gotten so far to where my index page www.example.com simply gives me a file listing because of the missing index.php file, while every url request with a path is working.
How can I accomplish this with .htaccess without ending up with a directory listing on my landing page?
This is as far as I got:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /api/init.php?path=$1 [NC,L,QSA]
Well your site root is a directory, so this rule you have excludes existing directories. What you could do is only exclude existing files, and allow existing directories to be handled by the PHP script. Like this:
RewriteEngine on
RewriteCond %{REQUEST_URI} !=/api/init.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /api/init.php?path=$1 [L,QSA]
I removed the NC flag as it's not needed. I added a condition to prevent an unnecessary file-system check.
You don't have to pass the path on in a URL parameter, as you could get it from $_SERVER['REQUEST_URI'] in PHP (not the same as REQUEST_URI in mod_rewrite, in PHP it always has the original URI). If you wanted to do that then your rule becomes nice and simple:
RewriteEngine on
RewriteCond %{REQUEST_URI} !=/api/init.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /api/init.php [L]
Because the query string will just be passed on unaffected (so QSA is not needed).

RewriteRule for unknown directory

So I'm trying to get a mod_rewrite rule to redirect requests to a php-script with an .htaccess file. The thing is, I want it to work regardless of where I put the project on a webserver (the .htaccess file and the php-script are always in the same folder).
The rewrite itself is very simple. If the script and the .htacess are in the directory /path/to/project and the user visits:
/path/to/project/somestring
it should be rewritten to:
/path/to/project/index.php?t=somestring
This should work for every subdirectory at any level in the webserver. So:
If the php-script and the .htaccess files are in the root:
/somestring2
should be rewritten to:
/index.php?t=somestring2
If the php-script and the .htaccess file are in /subdirectory:
/subdirectory/somestring3
should be rewritten to:
/subdirectory/index.php?t=somestring3
So the RewriteRule should perform the same rewrite action regardless of where the project lives within the server. The string that is to become a GET-parameter can consist of those characters: [a-zA-Z0-9]. If there are other GET-parameters in the requested URL, they should be appended as well (hence the QSA flag). This is what I've tried:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*/)([a-zA-Z0-9])/? $1index.php&t=$2 [L,QSA]
However, this results in a 404 error. How can I alter it to do what I want?
Try :
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*/)?([A-Za-z0-9]+)/?$ /$1index.php?t=$2 [NC,L,QSA]
Note that a leading slash in rewrite pattern is not required in the RewriteRule context.

RewriteRule doesn't exclude existing files/directories

So for a website I have an index.php file in my root directory that expects url parameters to determine which contents to echo:
example.com/?p=home
example.com/?p=blog
However, for aesthetical purposes, I wrote a RewriteRule in the .htaccess file in the root directory that redirect the following requests to the index.php in the root directory with URL parameters:
example.com/home → example.com/?p=home
example.com/blog/ → example.com/?p=blog
The rule looks like this:
RewriteEngine On
# This is my rule
RewriteCond %{REQUEST_URI} !^(bootstrap|cms|php|pics|project|install)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ([a-zA-Z]*)/?$ index.php?p=$1 [NC]
# The following rules are used by the CMS I'm using, so I don't want to change those
RewriteRule ^(cms)($|/) - [L]
RewriteRule ^(install)($|/) - [L]
RewriteCond %{REQUEST_URI} !^(\.png|\.jpg|\.gif|\.jpeg|\.bmp)/$
RewriteRule (.*.html|.*.php|.*.htm) cms_worker.php?page=$1 [QSA]
However, using this all CSS and JS files that I included in the index.php using relative links (e.g. <link href="/project/style.css" rel="stylesheet">) aren't loaded as the rule seems to effect those requests as well.
Why is this happening? And how can I solve it? I thought the Rewrite Conditions above the rule should prevent the Rule from being applied when the requested directory or file exists ... I tried adding the first RewriteCond to exclude requests to the specified directories from the Rule, but that didn't work as well.
Edit: It works fine if I access a page without the trailing slash (i.e. example.com/blog). However, when I access the page with the trailing slash (i.e. example.com/blog/), the CSS and JS files aren't being loaded. The network tab of the Chrome dev tools doesn't show anything, but when I open the source code and click on the relative link to the css file, it redirects to example.com/blog/project/style.css instead of example.com/project/style.css
I believe this is due to regex pattern used in your rules. Try these rules:
RewriteEngine On
# This is my rule
RewriteCond %{REQUEST_URI} !^(bootstrap|cms|php|pics|project|install)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([a-z]+)/?$ index.php?p=$1 [NC,L,QSA]
# The following rules are used by the CMS I'm using, so I don't want to change those
RewriteRule ^cms($|/) - [L,NC]
RewriteRule ^install($|/) - [L,NC]
RewriteRule ^(.+?\.(?:html?|php))$ cms_worker.php?page=$1 [QSA,L,NC]
To resolve relative links add <base href="/"> in <head> section of your HTML.

RewriteCond Being Ignored?

I am trying to use mod_rewrite on a Ubuntu 12.04 server to make my URLs more readable, however I want to add an exception for images and css files.
My input URLs are in the format \controller\action which is then re-written to index.php?controller=controller&action=action. I want to add an exception so that if an image or css file is specified, the URL is not re-written, e.g. \images\image.jpg would not be re-written.
My .htaccess code is as follows:
RewriteEngine on
RewriteCond %{REQUEST_URI} !(\.gif|\.jpg|\.png|\.css)$ [NC]
RewriteRule ^([a-zA-z]+)/([a-zA-z]+)$ test.php?controller=$1&action=$2 [L]
RewriteRule ^([a-zA-z]+)/([a-zA-z]+)/([^/]*)$ test.php?controller=$1&action=$2&$3 [L]
My re-write code is working fine and the URLs are coming out as intended, however even if I request an image, the URL is still being re-written. It appears that my RewriteCond is being ignored, anyone any suggestions as to why this might be?
The RewriteCond only applies to your first RewriteRule, it should be reproduced for the second rule. However, I think that is better to add a non-rewriting rule, before, to exclude existing stuffs.
# Do nothing for files which physically exist
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [L]
# your MVC rules
RewriteRule ^([a-zA-z]+)/([a-zA-z]+)$ test.php?controller=$1&action=$2 [L]
RewriteRule ^([a-zA-z]+)/([a-zA-z]+)/([^/]*)$ test.php?controller=$1&action=$2&$3 [L]
The rewriteCond rule is only applied for the next RewriteRule.
So you need to at least repeat the rewriteCond for your seconde RewriteRule.
No there is certainly better things to do.
For example a usual way of doing it is to test that the url is matching a real static ressource. If all your php code is outside the web directory (in libraries directory, except for index.php) then all styatic ressources available directly on the the document root can only be js files, css files, or image files.
So this is the usual way of doing it:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([a-zA-z]+)/([a-zA-z]+)$ test.php?controller=$1&action=$2 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([a-zA-z]+)/([a-zA-z]+)/([^/]*)$ test.php?controller=$1&action=$2&$3 [L]
But this is a starting point. We could certainly find something to avoid doing 2 rules for this (maybe I'll have a look later)

mod_rewrite picking up directories that exist

I have the following .htaccess in the root folder of a web application:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^\.(.*) [NC]
RewriteRule ^(.*) http://%1/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ index.php?req=%{REQUEST_URI}&%{QUERY_STRING}
This works as I want with one exception: if the directory actually exists, that directory is served rather than the request being passed of to the PHP script.
So if directory /docs/ exists, the index.htm is sent to the browser whereas a request for the directory /doesnotexist/ which doesn't exist will be handed to the PHP script.
Any ideas how I can amend this so that ALL requests are handed off to PHP regardless of whether the location exists or not?
Your second set of conditions constrain matching requests to not point to an existing file nor an existing directory. That's why when you browse to /docs/ and the directory exists the rule won't fire.
All you need to do to fix this, is to get rid of the second rewrite condition. This let's the rule beneath it fire on any request that does not point to an existing file. If, indeed, what you want is to redirect every request to the PHP script, then the first rewrite condition needs to be eliminated as well.
RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ index.php?req=%{REQUEST_URI}&%{QUERY_STRING}
Note that the # uncomments the line so it won't get picked up by mod_rewrite.