How to fwd urls to existing paths AND one more path with apache's mod_rewrite? - apache

My current .htaccess looks like this:
RewriteEngine On
# RewriteCond %{REQUEST_URI} !^/_project
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^.*$ index.php [QSA,L]
The uncommented lines are pretty straightforward:
The two Conds make sure the Rule isn't applied to existing files (!-f) and folders (!-d).
The Rule sends everyting else to index.php
The uncommented lines I took from somewhere. I believe it's the best way to do what I require: 'pretty urls'.
Basically it works. Existing files (e.g. /css/general.css) are requestable and non-existing paths (e.g. /admin/login) are routed to index.php. Existing and non-existing paths must be able to work 'amongst eachother': /css/all.css is sometimes a buffered existing css file and sometimes (when it doesn't exist) it's handled by PHP. /css/general.css is always a file. /css/club_N.css (N is a number) is always a PHP script.
/_project/ is an existing folder with Basic HTTP Auth protection. For instance /_project/phpinfo.php works as well. In the _project folder I have created a (valid) symlink to the backups folder: /_project/backups/. Somehow the (existing) files in the backups folder can't be reached. For instance /_project/backups/today.bz2 is routed to index.php =( The same happens with either or both commented lines uncommented.
What's wrong with the htaccess config? If I remove the Rewrite stuff entirely, I get a 403 Forbidden. Probably something with the .htaccess in the _project folder (?).
PS. Obviously I can't show you the actual website. People wouldn't like it if you could download their backups =)

.htaccess files are hierarchical in scope, any such files in parent directories apply to their children.
The Basic Auth in /_project/ will apply to subdirectories unless you switch it off in those directories, as will the RewriteRule declaration. Often it is wise to add RewriteEngine off in the .htaccess of the child directory structure to stop the rules applying there, or possibly add a conditional blocking that structure on the original rule set.

Related

Why isn't this rewrite rule catching?

I have a website with a bunch of assets using absolute paths, ie:
<img src=\"/_files/images/shell/logo.jpg\">
My issue is, the url for this site in development is: http://arraybox.net/~hazelwoodconstru/
Unfortunately, this means it is trying to access the files at arraybox.net/_files/images/shell/logo.jpg instead of arraybox.net/~hazelwoodconstru/_files/images/shell/logo.jpg
I'm trying to handle this with an .htaccess RewriteRule.
RewriteRule /_files/(.*) /~hazelwoodconstru/_files/$1 [R,NC,L]
I had it working at some point, but I messed around with it, and now it's broken and I can't figure out why!
EDIT:
I also have the following in this .htaccess which may or may not be interfering:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !404\.shtml|500\.shtml|favicon\.ico|index\.php{1}
RewriteRule (.*) /~hazelwoodconstru/ [QSA,L]
You need to make the rule's matching more strict, as well as removing the leading slash:
RewriteRule ^_files/ /~hazelwoodconstru/%{REQUEST_URI} [L,R]
Remove the ,R flag if you don't want an external redirect.
What seems to be happening is that the absolute file paths just plain old won't use arraybox.net/~hazelwoodconstru/filepath - they go straight to arraybox.net/filepath. Writing the .htaccess on the main directory arraybox.net won't work for me, because there will be multiple sites using this setup, and there is no way to tell which /home/ directory the assets really live in.
The solution I went with:
Instead of using the user directory arraybox.net/~hazelwoodconstru, I set up a subdomain
hazelwood.arraybox.net.
I then edited the vhosts config to change the document root for this subdomain to point to the /home/hazelwoodconstru directory (I also had to change the user).
Additionally I had to modify vhosts.local to change phpopenbasedirprotect to use documentroot. Then I had to add allow from all on the /home/ directory.
It's now working on the subdomain.

.htaccess - Redirect all to index.php for root folder or subfolder

I need an .htaccess file that will work whether it is put in the root folder or a subfolder without modification. The script below is the normal one that I've been trying to adapt without success. I tried the solution on htaccess rewrite index.php on root and subfolders and couldn't get it to work.
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>
Layout
.htaccess
index.php
subfolder1
- .htaccess
- index.php
The route /blah should go to /index.php and /subfolder1/whatever should go to /subfolder1/index.php. Currently, the above script will send /subfolder1/whatever to /index.php.
[Update]
This should also work for any path under subfolder1, like /subfolder1/1/2/3/idunno.
If you are using Apache 2.2.16 and later, you can just stop using mod_rewrite, which although extremely useful and powerful, can get messy as hell.
A new directive in mod_dir was introduced, FallbackResource which does just that, redirecting to the uri of your choice if there is no hit on the file system. It is available in .htaccess files as long as AllowOverride Indexes is specified for the directories in the configuration.
As .htaccess files are evaluated depth-first, you just have to have each .htaccess file describe your fallback resource in the current directory, and the one in the subdirectory subfolder1 will take precedence:
subfolder1/.htaccess:
FallbackResource index.php
.htaccess:
FallbackResource index.php
They're both the same, and work just right.
It seems this directive is not well known yet even though it's been around for a few years, and its goal is precisely to solve that problem in an elegant way.
There is only one limitation with that setup. Calling urls in non-existing sub-directories of the root dir or subfolder1 will yield subrequest recursion and subsequently an error 500, because the fallback resource is local to the given directory.
The best approach is to have absolute uris (beginning with '/') as parameter to FallbackResource, which is why it is true that the requirement in itself is kind of odd, and is probably not playing too well with the inner workings of Apache.

Apache does not rewrite request if file on path exists

I'm doing a rewrite with mod_rewrite on every request that does not match an existing file or directory. This is my configuration:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [NC,L]
This is used to map URLs like /abc/foo or /abc/foo/10 to my app. And it works just fine.
To improve the performance, my app now stores the results of a call to /abc/foo in a file foo in the corresponding directory /abc - so that after the first call the rewrite conditions do no longer apply (file does not exist) and apache directly serves the data without first invoking the app. Works fine as well.
The problem is: Requesting /abc/foo/10 does now no longer cause the URL to get rewritten, instead I get an error "404 File Not Found". The log entries state that the rewrite condition !-f is no longer true, but actually the file /abc/foo/10 does not exist. /abc/foo exists, but is a file, not a directory.
How can I get this to work?
(MultiViews is disabled)
This is because foo exists as a file and apache serves foo with the additional /10 passed as a query string. So, your application should write some additional code to the foo file, that also checks if a request includes some additional url component and then handle creation of the directory "foo" and the file 10.
You must be in per-dir/htaccess context w/ AcceptPathInfo on.
Therefore REQUEST_FILENAME matched the part that existed, and is not the same as REQUEST_URI.
Use the REQUEST_URI var if you don't care where the request was previously mapped in your rewritecond.
In per-vh context, these vars are always the same.
Project design is a little bit wrong - others already pointed out that it's not scallable - how could You cache a request to /abc/foo/10 if there is already a /abc/foo file?
Answer to that and to Your problem is usage of subfolders instead of files.
So instead of cache structure of:
/abc/foo
/abc/bar
...?
use:
/abc/index.html
/abc/foo/index.html
/abc/bar/index.html
/abc/foo/10/index.html
and each time create new directory with index.html
This time Apache would find out that there is /abc/foo folder but no /abc/foo/10 file in it, so RewriteCond will apply.
edit
You could also try a different way - to modify url with mod_rewrite, changing urls:
/abc/foo
/abc/bar
/abc/foo/10
to something like:
/cache/abc~foo
/cache/abc~bar
/cache/abc~foo~10
htaccess rules (roughly):
# redirecting to cache folder and removing last '/'
RewriteCond %{REQUEST_URI} ^/(abc|cde)
RewriteRule ^(.*?)/?$ /cache/$1 [L]
# recursive replacing '/' with '~'
RewriteCond %{REQUEST_URI} ^/cache/.*/
RewriteRule cache/(.*)/(.*)$ /cache/$1~$2 [L]
Your standard htaccess rules should follow

.htaccess mod_rewrite issue

Almost in any project I work on, some issues with .htaccess occur. I usually just find the easiest solution and leave it because I don't have any knowledge or understanding for Apache, servers etc. But this time I thought I would ask you guys.
This is the files and folders in my (simplified) setup:
/modrewrite-test
.htaccess
/config
/inc
/lib
/public_html
.htaccess
/cms
/navigation
index.php
edit.php
/pages
index.php
edit.php
login.php
page.php
The "config", "inc" and "lib" folders are meant to be "hidden" from the root of the website. I try to accomplish this by making a .htaccess-file in the root that redirects the user to "public_html". The .htacess-file contains this:
RewriteEngine On
RewriteRule (.*) public_html/$1
This works perfect. If I type "http://localhost/modrewrite-test/login.php" in my browser, I end up in public_html/login.php which is my intention. So this works fine. The .htaccess-file in "public_html" contains this:
RewriteEngine On
# Root
RewriteRule ^$ page.php [L]
# Login
RewriteRule ^(admin)|(login)\/?$ login.php [L]
# Page (if not a file/directory)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ page.php?url=$1 [L]
The first rewrite just redirects me to public_html/page.php if I try to reach "http://localhost/modrewrite-test/". The next rewrite is just for the convenience of users trying to log in - so if they try to reach "http://localhost/modrewrite-test/admin" or "http://localhost/modrewrite-test/login" they will end up at the login.php-file. The third and last rewrite handles the rest of the requests. If I try to reach "http://localhost/modrewrite-test/bla/bla/bla" it will just redirect me to public_html/page.php (with the 'url' GET-variable set) instead of finding a folder called "la", containing a folder named "bla" and etc.
All of these things work perfect but a minor issues occurs when I for instance try to reach "http://localhost/modrewrite-test/cms/navigation" without a slash at the end of the URL. When I try to reach that page the browser is somehow redirected to "http://localhost/modrewrite-test/public_html/cms/navigation/". The correct page is shown but why does it get redirected and add the "public_html" part in the URL? The desired behavior is that the URL stays intact and that the page public_html/cms/navigation/index.php is shown.
The files and folders in the (simplified) can be found at http://highbars.com/modrewrite-test.zip
I ran into the same problem with "strange" redirects when trying to access existing directory without slash at end. In my case this redirection was done by mod_dir Apache module. To disable redirection I used DirectorySlash directive. Try putting in .htaccess files following string:
DirectorySlash Off
RewriteBase may help. Try this in public_html/.htaccess:
RewriteEngine On
RewriteBase /
Add the following to /modrewrite-test/.htaccess:
RewriteBase /modrewrite-test
Just to be on the safe side, I'd add the same rule also to /modrewrite-test/public_html/.htaccess. I found that having RewriteBase always set prevents a lot of potential problems in the future. This however means that you might need to update the values if you change the URI structure of your site.
Update:
I don't think that this is possible with your current folder structure. I believe that the problem is that existing subdirectories prevent rewrite rules from firing. Note the behavior please - everything works fine while you are working with non-existent files and directories, thanks to these two conditions:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
However if you try to open any index file from an existing subdirectory, you get redirected to .../public_html/.... Since you can properly open /modrewrite-test/cms/navigation/edit.php, I can only assume that the request is being overwritten by some Apache core directive, which adds slashes at end of folder URLs. Notice that everything works fine if you have an ending-slash at each URL (i.e. the Apache core directory does not need to "correct" your URL, thus everything gets rewritten by your own rewrite rules).
Suggested solution (unless anyone can advise better):
Change /modrewrite-test/public_html/.htaccess as follows:
RewriteEngine On
RewriteBase /modrewrite-test
# Page (if not a file/directory)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ page.php?url=$1 [L]
Then Remove all PHP files from subfolders and use the Front Controller pattern, i.e. route all requests through your main page.php file and do not delegate anything down below.
You can then use the Factory pattern to initiate individual UIs (i.e. navigation/edit.php) directly from your main page.php file based on contents of $_GET['url'] (make sure to properly sanitize that).
Update #2:
This other post on StackOverflow advises on project structure used by Zend Framework - it essentially shows the approach which I suggested above. It is a valuable information asset regardless if you use Zend Framework or not.

Building path independent mod_rewrite statements for generic .htaccess file

Say I have three small web applications stored under a shared web root:
www.example.com/app1/
www.example.com/app2/
www.example.com/app3/
www.example.com/app4/
each application has a .htaccess file containing some run-off-the-mill mod_rewrite statements to rewrite urls like
RewriteCond %{REQUEST_URI} ^/app1/([^/]+)/([^/]+)\.html$
RewriteRule .* /app1/index.php?selectedProfile=%1&match=%2&%{QUERY_STRING}
now, I would like to have a generic .htaccess file in each /app{n} directory. So, no RewriteBase and no /app{n} prefix in the RewriteConds.
One idea I had was making the first level a wildcard directory as well:
RewriteCond %{REQUEST_URI} ^/([^/]+)/([^/]+)/([^/]+)\.html$
seeing as the .htaccess file gets triggered only when the /app{n} directory is entered, this should work.
Is this an acceptable solution?
Are there other, better ones?
You don’t need to specify the full path. You can use relative paths that are then resolved from the base path.
So try this rule:
RewriteRule ^([^/]+)/([^/]+)\.html$ index.php?selectedProfile=$1&match=$2 [QSA]
You could even use just this single rule in your document root directory:
RewriteRule ^(app\d+)/([^/]+)/([^/]+)\.html$ $1/index.php?selectedProfile=$2&match=$3 [QSA]