mod_rewrite troubles - apache

I'm trying to rewrite requests for files that exist, regardless of their extension, in a public directory to that directory, and everything else to a controller. If the user goes to http://example.com/images/foo.gif, and it exists, the image should be served from %{DOCUMENT_ROOT}/public/images/foo.gif. If they go to http://example.com/foo/bar, and it doesn't exist then the request should be routed through index.php. What I have so far is two blocks that work separately, but not together. When both are put in .htaccess, whichever one is first in .htaccess works perfectly, and the one on the bottom is completely ignored (it gives a 404 page when I try to test it). Can someone please explain to me what I'm doing wrong?
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} -f
RewriteRule ^.*$ - [L]
RewriteRule ^.*$ index.php [L]
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} !-f
RewriteRule ^.*$ - [L]
RewriteRule ^(.*)$ %{DOCUMENT_ROOT}/public/$1 [L]

It looks like there's a few things wrong.
It looks like your RewriteCond's are backwards. If %{DOCUMENT_ROOT}/public/%{REQUEST_URI} doesn't exist (!-f) then you want to rewrite to index.php, but if it does exist (-f) then rewrite to /public/$1. The second thing is the RewriteRule ^.*$ - [L] is actually preventing the actual rule from being applied because it ends with [L] and that stops the rewriting in the current iteration.
Even if you remove the ^.*$ - [L] rewrites and flip the -f and !-f, you run into a problem with the 2nd iteration of rewrites:
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} !-f
RewriteRule ^.*$ index.php [L]
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} -f
RewriteRule ^(.*)$ %{DOCUMENT_ROOT}/public/$1 [L]
This is what happens when you try to access http://example.com/foo/bar :
%{DOCUMENT_ROOT}/public//foo/bar doesn't exist, !-f condition met
foo/bar is rewritten to index.php, with [L], end rewrite
The request is INTERNALLY redirected to index.php
With a new URI (index.php) all rules are re-applied
%{DOCUMENT_ROOT}/public/index.php exists, !-f condition failed
%{DOCUMENT_ROOT}/public/index.php exists, -f condition met
index.php gets rewritten to %{DOCUMENT_ROOT}/public/index.php
INTERNAL redirect, and all rules are reapplied to new URI (/public/index.php)
%{DOCUMENT_ROOT}/public//public/index.php doesn't exist, !-f condition met
public/index.php is rewritten to index.php
go back to 3. internal loop
Something similar happens when you try to access http://example.com/images/foo.gif , essentially, you need to get the other rule to stop rewriting the 2nd time around. So you need to add a 2nd set of conditions:
RewriteCond %{REQUEST_URI} !^/public/
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} !-f
RewriteRule ^.*$ index.php [L]
RewriteCond %{REQUEST_URI} !/index.php
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} -f
RewriteRule ^(.*)$ %{DOCUMENT_ROOT}/public/$1 [L]

Related

Not able to track htaccess issue, How to implement IF ELSE in htaccess

I have a problem with htaccess to execute a rule and if rule matches then do not check for another rule. which i am trying to figure out from last few hours. Below is the sample code
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/projectname/$
#RewriteCond %{REQUEST_URI} !projectname/(storage)$
RewriteRule ^(.*)$ /projectname/abc/dist/$1 [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} projectname/(storage)$
RewriteRule ^(.*)$ /projectname/storage/$1 [L]
Rewritecond %{REQUEST_URI} projectname/(.*)$
#RewriteCond %{REQUEST_URI} !projectname/(storage)$ [NC]
RewriteCond %{REQUEST_URI} !\.(html?|png|woff|ttf|eot|svg|woff2|jpg|gif|xml|rss|png|css|js|json)$ [NC]
RewriteRule ^(.*)$ /projectname/#/$1 [NE,R=301,L]
RewriteRule ^(.*)$ /projectname/abc/dist/$1 [L]
I want to load everything from projectname/abc/dist folder but not for the case when i have storage inside url then i want to load the data from storage folder only.
So as per rules defined here everything works fine but when i have storage inside url/src for image it still checks /projectname/abc/dist/storage/xxxx.png instead of checking /projectname/storage/xxxx.png as defined in htaccess rule.
I have tried using [S=5] skip with storage rule in htaccess does not works from reference http://httpd.apache.org/docs/2.4/rewrite/flags.html.
Also as per my understanding [L] is the last so it should stop the htaccess after storage rule but it does not.
I have tried implementing IF ELSE in htaccess but the examples i tried for IF ELSE does not help even.
reference https://blogs.apache.org/httpd/entry/new_in_httpd_2_4
reference http://httpd.apache.org/docs/2.4/rewrite/flags.html (check Skip IF ELSE stanza)
Any Idea would be useful.
You want to load from 'projectname/abc/dist'-folder EXCEPT when there is 'storage' anywhere in the requested URI?! If I got that right, that might help:
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !storage
RewriteRule ^(.*)$ /projectname/abc/dist/$1 [END,P]
I added [P] for 'proxy' assuming that you don't want to show the customer he is rewritten by your server. If you don't mind seeing them, just leave that out.
First of all, L|last doesn't mean stop all processing.
If you are using RewriteRule in either .htaccess files or in sections, it is important to have some understanding of how the rules are processed. The simplified form of this is that once the rules have been processed, the rewritten request is handed back to the URL parsing engine to do what it may with it.
See also Ruleset Processing for how this works.
To load from /project/abc/dist, except when there's storage in the URL, you must first check for storage
RewriteCond %{REQUEST_URI} /storage/
RewriteRule /([^/]*$ /project/storage/$1 [L]
And then
RewriteRule ^(.*)$ /project/abc/dist/$1 [L]
Finally, to prevent a rewrite loop, prefix the rules with
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
Everything together
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
RewriteCond %{REQUEST_URI} /storage/
RewriteRule /([^/]*$ /project/storage/$1 [L]
RewriteRule ^(.*)$ /project/abc/dist/$1 [L]

Apache Rewrite urls - remove .html and return 404 if .html is present

I am adding a directory to a website that is served with Apache 2 that I want to drop the .html extension from incoming requests. In /new-directory I have a .htaccess file containing:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.html [NC,L]
So with this rule /new-directory/page works, but /new-directory/page.html also works, which I don't want. I want all pages in new-directory/ and sub-directories to only serve pages without .html, and return a 404 not found if a page.html request comes in.
These are new pages so I don't care about redirecting.
Edit:
Forgot to mention that there is only one file in /new-directory (/new-directory/dhandler) - a Perl script that parses the incoming url if there is a matching database entry. There are no files to match so I can drop that condition.
Figured it out, my fault that I didn't explain that there is only one default file handler in /new-directory which lead to some confusion, see edit above - anyway this worked:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^\.]+)$ $1.html [NC,L]
RewriteCond %{THE_REQUEST} .*\.html[\s\?]{1}
RewriteRule .*\.html$ - [R=404,L]
What messed me up was that sometimes these urls will have query strings so I have to check for both space and ? to match .html in %{THE_REQUEST}.
EDIT: If someone has actual .html files in a directory(instead of one file that dynamically handles all requests like I do) then they should probably add:
RewriteCond %{REQUEST_FILENAME} -f
right after 'RewriteEngine On' To make sure that the incoming request matches an existing file in /new-directory.
You need a couple of rules to do this:
RewriteBase /
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule (.*) $1.html [NC,L]
RewriteCond %{THE_REQUEST} .*\.html\s
RewriteRule .*\.html$ - [R=404,L]
The first one checks that there is actually an html file that corresponds to the request. If it does, it will internally rewrite the request to that.
The second rule will redirect anything that ends .html to a 404 not found.
Place this code in /new-directory/.htaccess:
RewriteEngine On
RewriteBase /new-directory/
RewriteCond %{THE_REQUEST} /(?:index)?(.*?)\.html[\s?] [NC]
RewriteRule ^ %1 [R=301,L,NE]
RewriteCond %{REQUEST_FILENAME} !-d
#RewriteCond %{DOCUMENT_ROOT}/new-directory/$1\.html -f [NC]
RewriteRule ^(.+?)/?$ $1.html [L]

How can I redirect existing *.phps with this mod_rewrite, too?

I use the following mod_rewrite to redirect everything to the index.php, but it seems like that if an .php file exists it is opened instead of redirected to the index.php. What should I change to solve this ?
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^(data/|js/|styles/|robots\.txt) - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
PS: I know there are plenty of examples to redirect .php to something else, but I would like to do it without creating a new RewriteRule if possible.
Not sure what you are trying to achieve with the first 4 lines (they look to be doing what apache would do by default). They are probably what are causing the problem.
Whole block should be
RewriteRule ^(data/|js/|styles/|robots\.txt) - [NC,L]
RewriteRule ^/(.*)$ index.php [NC,L]
That will send every except your asset dirs to index.php
If you want an actual http redirect, you need
RewriteRule ^/(.*)$ /index.php [R]
The following is specific to php, if that's all you're interested in
RewriteRule ^(.*)\.php index.php [NC,L]

Check URL and stop processing using htaccess

I want to check URL using htaccess. Developer might want run special file - specialfile.php. I use htaccess:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} /specialfile\.php$
RewriteRule .* [L] #don't change adress
RewriteRule ^$ public/index.html [NC,L]
RewriteRule (.*) public/$1 [NC,L]
My idea was: if rewriteCond %{REQUEST_URI} !/specialfile.php$ true than htaccess should use RewriteRule .* [L] - that should mean that specialfile.php will be run and this all. But it doesn't work because it runs next rule: RewriteRule (.*) public/$1 [NC,L].
I think you are using the RewriteCond not correctly. The conditions only affect the next RewriteRule that follows.
Check out the example on the Apache Homepage. Since your 2nd RewriteRule is evalutated, I think your conditions are not correct. To get a litte bit more information about the rewriting, you should increase the log level. This is also documented here.
Your 2nd rule ^$ matches only an empty request btw. That's why it probably does not work as you expect it to.

mod_rewrite seems to ignore [L] flag

I'm trying to use the [L] flag in RewriteRule, but it doesn't seem to work. I'd like that if you call the page:
www.domain.com/admin/
it redirects you to:
www.domain.com/backend.php
Otherwise, if you call any other page (except for some pages) it redirects to:
www.domain.com/index.php
Here is what I have so far:
RewriteEngine on
RewriteRule ^admin/(.*) /backend.php/$1 [L]
RewriteCond $1 !^(index\.php|admin|assets|images|uploads|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]
If I use only the first rule, it works (but obviously doesn't redirect other pages to index.php). If I add the second rule, the server seems to ignore the first rule (I think it is "overwritten" by the second rule, even if I added the [L] flag)
This isn't how L works. The rewrite engine will continually loop through all the rules until the URI going into the engine is the same as the one coming out. The L just tells the rewrite engine to stop applying rules in the current loop iteration. So say you have:
RewriteRule ^(.*)$ /foo/$1 [L]
RewriteRule ^(.*)$ /bar/$1 [L]
after 1 iteration, given the URI /blah, I get /foo/blah because it stops rewriting after the first rule (but will still continue to loop). If I remove the L:
RewriteRule ^(.*)$ /foo/$1
RewriteRule ^(.*)$ /bar/$1
after 1 iteration, given the URI /blah, I get /bar/foo/blah. Both rules get applied, one after the other because the L isn't there to stop it.
You need to add a condition in your second rule to prevent it from rewriting the first, either one of these will do:
RewriteCond $1 !^(backend\.php|index\.php|admin|assets|images|uploads|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]
or:
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond $1 !^(index\.php|admin|assets|images|uploads|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]
Your second rule is strange. I do not know what you are attempting to do by putting $1 as the value you are checking your condition against. It should probably look like this:
RewriteEngine on
RewriteRule ^admin/(.*) /backend.php/$1 [L]
RewriteCond %{REQUEST_FILENAME} !^(index\.php|admin|assets|images|uploads|robots\.txt|backend\.php)
RewriteRule ^(.*)$ /index.php/$1 [L]
Note I have also added a pass-through for backend.php