htaccess rewrite if/else scenario? - apache

shortly: I want htaccess to decide, which of two css files to rewrite, depending on which one exists.
in detail: I want to include my css (later js, fonts, etc.) in the format vendor/project/style.css
<link rel="stylesheet" type="text/css" href="/vendor/project/style.css">
lets say that every project can be installed via composer, so it lands in the vendors folder. so the rewrite would easily be:
RewriteRule ^([a-zA-Z0-9_]*)/([a-zA-Z0-9_]*)/(.*)\.css$ vendor/$1/$2/$3.css [L]
BUT: you could also clone every project directly to be the root project, so it does NOT land in the vendors folder. the rewrite then should look like:
RewriteRule ^([a-zA-Z0-9_]*)/([a-zA-Z0-9_]*)/(.*)\.css$ $3.css [L]
my question is now: how may i tell apache to check which file actually exists? I tried something like:
RewriteCond %{REQUEST_FILENAME} ^([a-zA-Z0-9_]*)/([a-zA-Z0-9_]*)/(.*)\.css$
RewriteCond %{DOCUMENT_ROOT}/vendor/%1/%2/%3\.css -f
RewriteRule ^([a-zA-Z0-9_]*)/([a-zA-Z0-9_]*)/(.*)\.css$ vendor/$1/$2/$3.css [L]
RewriteCond %{REQUEST_FILENAME} ^([a-zA-Z0-9_]*)/([a-zA-Z0-9_]*)/(.*)\.css$
RewriteCond %{DOCUMENT_ROOT}/%3\.css -f
RewriteRule ^([a-zA-Z0-9_]*)/([a-zA-Z0-9_]*)/(.*)\.css$ $3.css [L]
but with no success at all.

Please try the following:
RewriteEngine On
# Check to see if file exists in `vendor`
RewriteCond %{REQUEST_URI} ^/(\w+)/(\w+)/(.+)\.css$
RewriteCond %{DOCUMENT_ROOT}/vendor/%1/%2/%3.css -f
RewriteRule ^ vendor/%1/%2/%3.css [L]
# Check to see if file exists in directory root
RewriteCond %{REQUEST_URI} ^/(\w+)/(\w+)/(.+)\.css$
RewriteCond %{DOCUMENT_ROOT}/%3.css -f
RewriteRule ^ %3.css [L]
You can check against REQUEST_URI, and no need to check again in the rules (note the % in the rewrite destination). Also, have changed your matching groups to use \w+ instead - this is shorthand for what you had before, with the exception that * has been replaced with + as the former would check for empty strings, too.
This has been tested on my local server, and works as expected.

Related

.htaccess excluding directory issue

I have a website consisting of a single index.html file. I have several menus, and need to go 3 levels deep, so I want a php-like structure as such: index.html?main=$1&secondary=$2&tertiary=&3. This has to be reduced to www.example.com/$1/$2/$3. Overall pretty simple I would think, using following rules for in .htaccess:
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)?$ index.html?main=$1&secondary=$2&tertiary=$3
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)?$ index.html?main=$1&secondary=$2
RewriteRule ^([A-Za-z0-9-]+)?$ index.html?main=$1
Now, I also have several folders in my root folder that shouldn't be affected, otherwise my include's wont work. Looking at this answer I've already tried exluding these folders using RewriteRule ^(bower_components|photos)($|/) - [L] before the other rules, but it didn't work. I've also tried this answer, making a .htaccess with contens RewriteEngine Off and putting it in my folders, also without success. So obviously I'm doing something wrong somewhere. To show my folder layout, here's a quick snapshot of it:
Here's my current .htaccess file:
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [L]
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)?$ index.html?main=$1&secondary=$2&tertiary=$3
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)?$ index.html?main=$1&secondary=$2
RewriteRule ^([A-Za-z0-9-]+)?$ index.html?main=$1
Now, if I go to http://localhost/myProject/activities, so 1 level deep as the index.html file is located in myProject, it does work and all includes are included correctly. However, when going to http://localhost/myProject/activities/test, I get to the basic index.html page, but my includes point to http://localhost/myProject/activities/bower_components/platform/platform.js, so the activities is too much.
Keep your rules like this:
Options +FollowSymLinks
RewriteEngine On
RewriteBase /myProject/
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [L]
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ index.html?main=$1&secondary=$2&tertiary=$3 [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ index.html?main=$1&secondary=$2 [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)/?$ index.html?main=$1 [L,QSA]
Then for css/js/image inclusion just use absolute path in your css, js, images files rather than a relative one. Which means you have to make sure path of these files start either with http:// or a slash /.
You can also try adding this in your page's HTML header: <base href="/myProject/" /> so that every relative URL is resolved from that URL and not the current URL.

Trouble with a mod_rewrite rule

I have a bit of a complicated rewrite scenario that I could use some expert advice on. I have a directory, "/directory" that contains some files. I would like to rewrite requests for files that don't exist in that directory to another directory which also exists, keeping the original directory in the URL and returning 404 for invalid paths. example
This exists:
/directory/file.php
This does not exist:
/directory/subdirectory/anotherfile.php
...but it does exist here:
/another_directory/subdirectory/anotherfile.php
So I would like
/directory/subdirectory/anotherfile.php to refer to /another_directory/subdirectory/anotherfile.php
... but if the folder/file doesn't exist in /another_directory/ the rule should return a 404.
I've tried various combinations but they all either don't return a 404 or they redirect. This is my latest attempt:
RewriteCond %{DOCUMENT_ROOT}/another_directory/$1 f [OR]
RewriteCond %{DOCUMENT_ROOT}/another_directory/$1 d
RewriteRule (.*) - [S=1]
RewriteRule ^directory/(.*)$ /another_directory/$1 [L, QSA]
If it helps to explain why I have this scenario, /another_directory/ is full of periodically regenerated static files that appear to be in the same directory as the existing files in /directory/ I am moving away from dynamic pages to generated static ones and don't want to mess with my serps and want to keep this large number of generated files isolated from the others.
Here's what I understood from your explanation:
/directory/subdirectory/anotherfile.php
This should be rewritten to
/another_directory/subdirectory/anotherfile.php
whether it exists or not. If I'm right, here's your answer, otherwise tell me more!
RewriteCond %{DOCUMENT_ROOT}/another_directory/$1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/another_directory/$1 -d
RewriteRule ^directory/(.*) another_directory/$1 [L,S=1]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (.*) - [L]
RewriteRule (.*) 404.php?uri=/$1&id=1 [L]

Dynamically serving files depending on whether they're written to file system

Terrible title, but don't really know how to describe the situation too well.
I would like to dynamically resize/crop images. I'm hoping to config my apache to do the following:
A request for /img/profile.jpg?crop&p=50 is received
Apache checks if the file /img/profile.c.50.jpg exists
If it does exist, it serves that up statically without having to hit my php server
If it doesn't exist, it hits /cropper.php?path=/img/profile.jpg&p=50
This file then writes the image to file, and serves it
Request comes in again (see step 1)
I feel like making use of the FilesMatch directive in the .htaccess file could be of us but I really don't know where to start. Any links would be appreciated, or any ideas.
Thanks all.
\Personally, I'd delegate this task to a script (maybe using X-Sendfile), rather than a rewrite mess.
Here you go, you may have to tweak your document root and make your "profile" part more general:
RewriteEngine on
RewriteCond %{QUERY_STRING} "(?:^|&)crop&p=50"
RewriteCond %{REQUEST_URI} ^(/img/profile)(\.png)$
RewriteCond /your/document/root%1.c.50%2 -f
RewriteRule ^ %1.c.50%2 [L]
# hand over to crop script if the above doesn't match
RewriteCond %{REQUEST_URI} =/img/profile.png
RewriteRule ^ /cropper.php?path=%{REQUEST_URI}&p=50 [L]
Solution always involving a script:
(accessed through /img/profiles/... for clarity)
$1 captures the profile name (in this case assumed to be 'a-z' only
$2 captures the desired width
$3 the file extension
Which really simplifies the rewrite:
RewriteEngine on
RewriteRule ^/img/profiles/([a-z]+)\.([1-9][0-9]+)\.(jpe?g|gif|png)$ /cropper.php?profile=$1.$3&p=$2 [L]
The script would check whether the file exists and either deliver it or generate it first.
Solution taking the image name from the requested URL:
(As you mentionned in your comment)
RewriteEngine on
RewriteCond %{REQUEST_URI} ^(/img/profile)\.([1-9][0-9]+)\.(png|gif|jpe?g)$
RewriteCond /your/document/root%{REQUEST_URI} -f
RewriteRule ^ %{REQUEST_URI} [L]
# hand over to crop script if the above doesn't match
RewriteCond %{REQUEST_URI} ^(/img/profile)\.([1-9][0-9]+)\.(png|gif|jpe?g)$
RewriteRule ^ /cropper.php?path=$1.$3&p=$2 [L]
which may be rewritten to the following, if the images are stored directly within your document root:
RewriteCond %{REQUEST_URI} ^(/img/profile)\.([1-9][0-9]+)\.(png|gif|jpe?g)$
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule ^ /cropper.php?path=$1.$3&p=$2 [L]

Redirecting via .htaccess to .php with arguments in current folder

I'm trying to redirect something like foo/bar to ?foo=bar, so I can do www.mydomain.com/hey/foo/bar to www.mydomain.com/hey/?foo=bar, but I can't seem to get the syntax right. I tried the following:
RewriteEngine on
RewriteRule ^foo/(.*)$ ?foo=bar [NC]
But this doesn't work. How would I accomplish this? I tried adding a forward slash behind the question mark, but that makes it link to the root directory.
Thanks,
Jengerer
You need to include the actual file you're rewriting to. Using ?foo=bar isn't pointing at any file in particular.
Use the following rule:
RewriteEngine on
RewriteRule ^(.*)/(.*)$ index.php?$1=$2 [NC]
Notice that I'm pointing to the file index.php. $1 is replaced by whatever is matched by the first (.*) and $2 by the second. So, if someone browsed to foo/bar, they would be taken to index.php?foo=bar.
Important Note: If you choose to use (.*) and accept any character for the variable name or value and plan to use this information in database queries, you'll want to be certain to escape this content properly using your database's escape functions (mysql_real_escape_string or pg_escape_string) or by using prepared statements.
If you're having problems with style or elements on the page not showing correct because you're using relative paths, you'll need to use absolute paths starting at the root. Otherwise, your pages style, images, etc. would break.
<link rel="stylesheet" type="text/css" href="/path/to/style.css" />
Maybe try removing the ^ from ^foo/(.*)$?
Well, it turns out that the problem wasn't in the redirection, but in the links that were made after the redirection.
After redirecting from /foo/bar to /?foo=bar, the CSS links and images were poorly matched because it was now looking for local links within the /foo/bar directory, which doesn't exist. What an interesting 'glitch'.
When you place the .htaccess in your root it should be like this:
RewriteEngine on
RewriteCond %{REQUEST_URI} !^$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\/]+)\/([^\/]+)\/([^\/]+)$ $1?$2=$3 [L]
when you place the .htaccess in your "hey" folder, it should be like this:
RewriteEngine on
RewriteCond %{REQUEST_URI} !^$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^\/]+)\/([^\/]+)$ ?$1=$2 [L]

RewriteRule checking file in rewriten file path exists

How can you use ModRewrite to check if a cache file exists, and if it does, rewrite to the cache file and otherwise rewrite to a dynamic file.
For example I have the following folder structure:
pages.php
cache/
pages/
1.html
2.html
textToo.html
etc.
How would you setup the RewriteRules for this so request can be send like this:
example.com/pages/1
And if the cache file exists rewrite tot the cache file, and if the cache file does not exists, rewrite to pages.php?p=1
It should be something like this: (note that this does not work, otherwise I would not have asked this)
RewriteRule ^pages/([^/\.]+) cache/pages/$1.html [NC,QSA]
RewriteCond %{REQUEST_FILENAME} -f [NC,OR]
RewriteCond %{REQUEST_FILENAME} -d [NC]
RewriteRule cache/pages/([^/\.]+).html pages.php?p=$1 [NC,QSA,L]
I can off coarse do this using PHP but I thought it had to be possible using mod_rewrite.
RewriteRule ^pages/([^/\.]+) cache/pages/$1.html [NC,QSA]
# At this point, we would have already re-written pages/4 to cache/pages/4.html
RewriteCond %{REQUEST_FILENAME} !-f
# If the above RewriteCond succeeded, we don't have a cache, so rewrite to
# the pages.php URI, otherwise we fall off the end and go with the
# cache/pages/4.html
RewriteRule ^cache/pages/([^/\.]+).html pages.php?p=$1 [NC,QSA,L]
Turning off MultiViews is crucial (if you have them enabled) as well.
Options -MultiViews
Otherwise the initial request (/pages/...) will get automatically converted to /pages.php before mod_rewrite kicks in. You can also just rename pages.php to something else (and update the last rewrite rule as well) to avoid the MultiViews conflict.
Edit: I initially included RewriteCond ... !-d but it is extraneous.
Another approach would be to first look if there is a chached representation available:
RewriteCond %{DOCUMENT_ROOT}/cache/$0 -f
RewriteRule ^pages/[^/\.]+$ cache/$0.html [L,QSA]
RewriteRule ^pages/([^/\.]+)$ pages.php?p=$1 [L,QSA]
To generalize the question: insert this above the rule that should not be matched if the file exists.
RewriteCond %{REQUEST_FILENAME} !-f
Sean Bright's answer provides a nice worked example for the caching question, but this line works more broadly. In my case, I have a link shortener where people can choose custom URLs and I didn't want it to be able to override existing files such as favicon.ico. Adding this line before the rewriterule fixed that issue.