.htaccess if file exists rewrite rule - apache

I found a couple of similar questions, but it seems like i need your help, because i'm not able to write it on my own.
I want this: http://domain.com/folder/file.123.ext (should work also without 123)
to be rewritten to /folder/file.min.ext (if it exists)
or /folder/file.ext (if the minified version doesn't exist)
It's for my css and js files ... so it will load non-minified versions for those files that doesn't have ones.
I currently have this:
RewriteRule ^(css|js)/([a-zA-Z_-]+)\.([0-9]+)\.(css|js)$ /$1/$2.min.$4
RewriteRule ^(css|js)/([a-zA-Z_-]+)\.(css|js)$ /$1/$2.min.$3
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(css|js)/([a-zA-Z_-]+)\.min\.(css|js)$ /$1/$2.$3
it works fine when i remove the second line ... but i need it to work also without the number :(

Can't you just replace the 2 rules with this?
RewriteRule ^(css|js)/([a-zA-Z_-]+)(\.[0-9]+)?\.(css|js)$ /$1/$2.min.$4

Following on from Jon's answer, this should solve the redirect loop problem:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(css|js)/([a-zA-Z_-]+)(\.[0-9]+)?\.(css|js)$ /$1/$2.min.$4
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(css|js)/([a-zA-Z_-]+)\.min\.(css|js)$ /$1/$2.$3
The first line will prevent the attached rule from being executed on redirect.
If you find any other issues or need to tweak this further, try using RewriteLog and RewriteLogLevel (at level 5) to debug the rule set and find out exactly what mod_rewrite is doing.

Related

Apache mod_rewrite path to query string for specific path format

I've been working on this for weeks. I want to be able to use a friendly URL to pass variables to a PHP script. For example, if someone uses this URL:
https://example.com/foo/bar/who
I would like my PHP script to receive this:
https://example.com/index.php/?var1=foo&var2=bar&var3=who
The catch is that I ONLY want to do this rewrite when there are three vars in the path. If there are fewer or more than three, I do not want to rewrite the URL.
I've seen several other explanations related to this type of rewrite but nothing quite like this.
This is almost working, but not quite. I only want the rewrite done if there's something present for those first three variables. I know this is incomplete, but with this method at least the REQUEST_URI contains the values I can parse. But again, I only want to do this when there are three vars in the path.
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !^index\.php/([^/]*)/([^/]*)/([^/]*)$ index.php [L]
Thank you.
You were pretty close, have it this way. Please make sure you clear your browser cache before testing your URLs. You had already created back references only thing you needed to use them, which I have added those now.
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]*)/([^/]*)/([^/]*)$ index.php?var1=$1&var2=$2&var3=$3 [L]

Redirect all to index.php using htaccess

I am writing a simple PHP-based MVC-ish framework. I want this framework to be able to be installed in any directory.
My PHP script grabs the request uri and breaks it off into segments. It makes segment 1 the controller and segment 2 the action. This goes all fine when I do this:
http://www.example.com/mvc/module/test/
It will go to the specific module controller and method. Now I have a default controller, the home controller, which is in folder home.
Now when I access this folder directly http://www.example.com/mvc/home/
It will display a 403 forbidden , because this folder does exist, instead it should also go back to http://www.example.com/mvc/index.php
If I would have installed the framework in a different folder, lets say folder framework it has to redirect back to http://www.example.com/framework/index.php
I would like to redirect every folder and php file back to the index.php, leaving everything else the way it is.
My first problem I encountered was it never redirects to the right folder, always to the domain root folder.
This is what I tried :
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
Your rewrite rule looks almost ok.
First make sure that your .htaccess file is in your document root (the same place as index.php) or it'll only affect the sub-folder it's in (and any sub-folders within that - recursively).
Next make a slight change to your rule so it looks something like:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?path=$1 [NC,L,QSA]
At the moment you're just matching on . which is one instance of any character, you need at least .* to match any number of instances of any character.
The $_GET['path'] variable will contain the fake directory structure, so /mvc/module/test for instance, which you can then use in index.php to determine the Controller and actions you want to perform.
If you want the whole shebang installed in a sub-directory, such as /mvc/ or /framework/ the least complicated way to do it is to change the rewrite rule slightly to take that into account.
RewriteRule ^(.*)$ /mvc/index.php?path=$1 [NC,L,QSA]
And ensure that your index.php is in that folder whilst the .htaccess file is in the document root.
Alternative to $_GET['path'] (updated Feb '18 and Jan '19)
It's not actually necessary (nor even common now) to set the path as a $_GET variable, many frameworks will rely on $_SERVER['REQUEST_URI'] to retrieve the same information - normally to determine which Controller to use - but the principle is exactly the same.
This does simplify the RewriteRule slightly as you don't need to create the path parameter (which means the OP's original RewriteRule will now work):
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L,QSA]
However, the rule about installing in a sub-directory still applies, e.g.
RewriteRule ^.*$ /mvc/index.php [L,QSA]
The flags:
NC = No Case (not case sensitive, not really necessary since there are no characters in the pattern)
L = Last (it'll stop rewriting at after this Rewrite so make sure it's the last thing in your list of rewrites)
QSA = Query String Append, just in case you've got something like ?like=penguins on the end which you want to keep and pass to index.php.
To redirect everything that doesnt exist to index.php , you can also use the FallBackResource directive
FallbackResource /index.php
It works same as the ErrorDocument , when you request a non-existent path or file on the server, the directive silently forwords the request to index.php .
If you want to redirect everything (including existant files or folders ) to index.php , you can use something like the following :
RewriteEngine on
RewriteRule ^((?!index\.php).+)$ /index.php [L]
Note the pattern ^((?!index\.php).+)$ matches any uri except index.php we have excluded the destination path to prevent infinite looping error.
There is one "trick" for this problem that fits all scenarios, a so obvious solution that you will have to try it to believe it actually works... :)
Here it is...
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,QSA]
</IfModule>
Basically, you are asking MOD_REWRITE to forward to index.php the URI request always when a file exists AND always when the requested file doesn't exist!
When investigating the source code of MOD-REWRITE to understand how it works I realized that all its checks always happen after the verification if the referenced file exists or not. Only then the RegEx are processed. Even when your URI points to a folder, Apache will enforce the check for the index files listed in its configuration file.
Based on that simple discovery, turned obvious a simple file validation would be enough for all possible calls, as far as we double-tap the file presence check and route both results to the same end-point, covering 100% of the possibilities.
IMPORTANT: Notice there is no "/" in index.php. By default, MOD_REWRITE will use the folder it is set as "base folder" for the forwarding. The beauty of it is that it doesn't necessarily need to be the "root folder" of the site, allowing this solution work for localhost/ and/or any subfolder you apply it.
Ultimately, some other solutions I tested before (the ones that appeared to be working fine) broke the PHP ability to "require" a file via its relative path, which is a bummer. Be careful.
Some people may say this is an inelegant solution. It may be, actually, but as far as tests, in several scenarios, several servers, several different Apache versions, etc., this solution worked 100% on all cases!
You can use something like this:
RewriteEngine on
RewriteRule ^.+$ /index.php [L]
This will redirect every query to the root directory's index.php. Note that it will also redirect queries for files that exist, such as images, javascript files or style sheets.
Silly answer but if you can't figure out why its not redirecting check that the following is enabled for the web folder ..
AllowOverride All
This will enable you to run htaccess which must be running! (there are alternatives but not on will cause problems https://httpd.apache.org/docs/2.4/mod/core.html#allowoverride)
just in case you were still wondering how to redirect all request either if the directory exists (for core framework folders and files) to the framework index handler, after some error/success attempts just noticed I just needed to change the RewriteCond in the .htaccess file
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
the above condition states "not found files" and "not found directories", ok, what if just remove "not found" (!-d) line, and ended with something like the below:
RewriteEngine on
RewriteBase /framework/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /framework/index.php [L,QSA]
It worked for me like a charm
I just had to face the same kind of issue with my Laravel 7 project, in Debian 10 shared hosting. I have to add RewriteBase / to my .htaccess within /public/ directory. So the .htaccess looks a like
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L,QSA]
After doing that don't forget to change your href in,
home
Example:
.htaccess file
RewriteEngine On
RewriteRule ^about/$ /about.php
PHP file:
about

Mod_rewrite rules from root

This problem has been bugging me for a while now.
I have a created a small site engine and I'm using mod_rewrite to tell the engine what page to proccess, SEO friendly links is a bonus :).
This is how it's works today:
the adress http://www.example.com/site/page
becomes http://www.example.com/engine.php?address=page
But what i want is:
the adress http://www.example.com/page
becomes http://www.example.com/engine.php?address=page
Everything works fine if i create a psuedo directory for the calls (/site) but when i try to do the same from the root strange things start to happends.
RewriteBase /
RewriteRule ^site/(.*) engine.php?%{QUERY_STRING}&address=$1
Works fine: /site/about/contacts becomes eninge.php?address=about/contacts
RewriteBase /
RewriteRule ^(.*)$ eninge.php?%{QUERY_STRING}&address=$1
Doesn't work, for some reason /about/contacts becomes eninge.php?address=eninge.php
(.*) means catch anything. Have you tried exluding files and directory before your catch-all ? Because it will cause an infinite recursion without it.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ eninge.php?%{QUERY_STRING}&address=$1 [L]
More information is available in the official documentation: http://httpd.apache.org/docs/current/mod/mod_rewrite.html
Update: You should also specify [L] at the end of your rule, to tell Apache to end the rewriting process here.
Check the RewriteLog (this has been updated in 2.4, check current docs if not using 2.2):
RewriteLog "/usr/local/var/apache/logs/rewrite.log"
RewriteLogLevel 3
This will show you exactly what mod_rewrite is doing and allow you to tune your configuration based on its output. Beware - it grows very quickly, and should never be used in production environments.
As an aside, you have some typos in your post - worth verifying that these differ from your config.
RewriteCond %{REQUEST_FILENAME} !^engine.php
RewriteRule (.*) engine.php?address=$1 [QSA,L]
Try this. What you have is causing the rewrite to loop around and first do engine.php?address=about/contacts as you were expecting, but then go around again and rewrite that to engine.php?address=engine.php. Make sense? The [QSA,L] is a Query String Append and Last flag that will add the query string to your URL and tell the rewrite engine to stop looking for rewrites. The RewriteCond %{REQUEST_FILENAME} !^engine.php is to check that you haven't already specified the engine rewrite by ensuring the current URL doesn't start with engine.php. This is necessary if you are writing this in an .htaccess file rather than the .httpd config files.

How do I get apache RewriteRule working correctly for a subdomain?

I just setup a subdomain with the following RewriteCond:
RewriteCond $1 !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?([^/]+)$ search.php?q=$1 [L,NS]
I'm using the same rewrite condition on my main domain and it works perfectly. However, when I set it up on the subdomain, it simply outputs "index.php" when going to http://sub.domain.com
Every page on the subdomain outputs the page name in the body instead of processing the code, except for the search page, which appears to be working correctly.
What can I do to correct this issue?
I haven't played with your exact regex with mod_rewrite, but if I was looking at writing that regex in another engine, I would have to escape the slash. Also, given that $ is used to indicate a back reference, would that need escaping too (would your $ symbols in the regex be necessary as there is likely to be more text in the URI and it is not matched at the end of a string)?
I would try
RewriteCond $1 !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?([^\/]+)$ search.php?q=$1 [L,NS]
One other thing. Normally $ at the end of a regex means "only match if this is the end of the string". So from that, if RewriteCond is matching on ^search.php$ but the URL is search.php?q=... then I would think that this wouldn't match because search.php is not the end of the string. So that would look like the following (assuming you don't need to change anything else from your original).
RewriteCond $1 !^search.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?([^/]+)$ search.php?q=$1 [L,NS]
In the main config the path always begins with / and you need an absolute path:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/([^/]+)$ %{DOCUMENT_ROOT}/search.php?q=$1 [L]
In an .htaccess you need a RewriteBase which is stripped from the url (no / in the Rule now) and the path is relative.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]+)$ search.php?q=$1 [L]
Several things come to mind here:
I have a few suggestions/comments/gotchas. Hopefully one of them is useful to you:
Make sure search.php isn't just echoing out its $_GET parameters. While this sounds obvious in retrospect, it's one of the more overlooked solutions.
RewriteRule works slightly differently when you specify it in a server configuration file than if you specify it in an .htaccess. Specifically, ^/ is wrong in a server config version as the entire URL is used (http://sub.domain.com/blah).
Make sure no other rewrite rules are being processed for this subdomain first, either in the main httpd.conf / apache2.conf or .htaccess.
Make sure RewriteEngine On appears in your configuration, as it is activated per-VirtualHost.
The NS flag will ignore redirects done using a relative Redirect or relative RewriteRule.
It sounds like the pattern '^/?([^/]+)$' may not be matching at all.
I'd activate RewriteLog, crank RewriteLogLevel to level 3 or above, and see if your pattern is matching at all. If not, start with a simpler pattern, and then work your way to a more complex pattern.
Or, something else is matching the pattern, so the request never gets to 'RewriteRule ^/?([^/]+)$' at all. You will see this in the RewriteLog.
I believe I recently had a problem where '^/' didn't match in certain cases on a Virtual Host. But '/' worked. The folks in the #httpd on Freenode.org helped me. If I can find this in my notes, I'll post it here.

Why won't this mod-rewrite rule work?

I have a development site on my machine at
localhost/~Jason/hfh/admin/?admin=collections
My .htaccess file is in the /hfh/admin/ directory. It says:
RewriteEngine On
RewriteBase /~Jason/hfh/
RewriteRule ^([A-Za-z0-9\-\_]*)$ index.php?admin=$1
But when I go to
localhost/~Jason/hfh/admin/collections
I get a "page not found" error. Can anyone tell me why?
(This is related to another question at this link.)
If you have the .htaccess file in /hfh/admin/ make that the base to begin with.
RewriteBase /~Jason/hfh/admin/
then you may see what you expect. Also you'll may want a clause to not redirect when the File/Directory exists.
Does typing the expected result URL work?
/~Jason/hfh/admin/index.php?admin=Collections
Edit:
So what happens if you change the whole lot to:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /~Jason/hfh/admin/index.php?admin=$1 [L]
It looks like that would be sending you to the page /~Jason/hfh/index.php?admin=collections, when you want /~Jason/hfh/admin/index.php?admin=collections.
Try changing the rule to:
RewriteRule ^([A-Za-z0-9\-\_]*)$ admin/index.php?admin=$1
The short, direct answer for now appears to be: you can't use mod_rewrite on your localhost.