Apache SetEnvIf and using %{ENV:...} - apache

My question is related to the use of SetEnvIf and how I can then use the environment variables correctly as I need them. I shall explain what I need to do, then what I already have...
I need to set the php application directory in my htaccess file, as some of the rewrite rules use that directory name, and I'm trying to allow the admin user the ability to change it. It is currently set in the php, and changing it is done manually in the htaccess file and then again in the php. To achieve this, I currently have the following (taking advice regarding using env vars in rewrite rules)
SetEnvIf SERVER_PROTOCOL ".*" APP_DIR=application
It's set up that way to get the env var set before any rewrite rules, as SetEnv is processed after any rewrite rules, whereas SetEnvIf is processed before.
Now I can access APP_DIR using %{ENV:APP_DIR} in rewrite conditions, and the second part of any rewrite rules.
What I need to achieve is a little more complex than that, as the application directory doesn't exist, but is rather a redirect to let the PHP application know to process as a back-end rather than a front-end, for example the rewrites currently (without the env var) are
### ADMIN REWRITE
RewriteRule ^application\/?$ index.php?mode=administration&%{QUERY_STRING} [NC,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^application(\/(.*\/?))?$ index.php?page=$1&mode=administration&%{QUERY_STRING} [NC,L]
### FRONTEND REWRITE - Will ignore files and folders
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*\/?)$ index.php?page=$1&%{QUERY_STRING} [L]
So, as you can probably see, I need to be able to substitute application for APP_DIR to let the administrators of the system change the path, so it could be cms or admin etc.
Sadly, the following won't work
### ADMIN REWRITE
RewriteRule ^%{ENV:APP_DIR}\/?$ index.php?mode=administration&%{QUERY_STRING} [NC,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^%{ENV:APP_DIR}(\/(.*\/?))?$ index.php?page=$1&mode=administration&%{QUERY_STRING} [NC,L]
### FRONTEND REWRITE - Will ignore files and folders
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*\/?)$ index.php?page=$1&%{QUERY_STRING} [L]
%{ENV:APP_DIR} cannot be used in the regex of a rewrite, as the regex is processed before any rewrites, and the env vars.
Has anyone ever achieved what I need? Can it be done, even if it means adjusting the rewrite logic somewhat? I must state that the php logic after the rewrites can't be changed as it's a long time existing system which would have backwards compatibility issues if it were.

Use RewriteCond to match %{REQUEST_URI} with the environment variable up to the first slash (/), then start your RewriteRule regex with ^[^/]*/. That will work if you're not dealing with subdirectories; otherwise, you'll have to forego a definitive starting point in your RewriteRule regexes.

Related

What is the best way to stop two rules redirecting endlessly?

I'm setting up a local apache server using XAMPP for use with CodeIgniter. My background is primarily in PHP and I have not had much experience with configuring apache or writing .htaccess files.
I am trying to remove the need for /index.php in the URL and redirect away from URLs that contain /index.php to the URL that doesn't.
However, when I run the two rules I have wrote to achieve this the page endlessly redirects. Individually they work as intended.
# Remove /index.php
RewriteCond %{REQUEST_URI} ^/index\.php/(.*)$ [NC]
RewriteRule .* http://%{HTTP_HOST}/%1 [L,R=301,QSA]
# Remap addresses to go to index.php
RewriteCond $1 !^(index\.php)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L,QSA]
Ultimately it should redirect from:
http://localhost/index.php/dashboard/example/
to
http://localhost/dashboard/example/
Which it does but it endless redirects afterwards.
You can use the END flag in the second rule, as per the docs:
Using the [END] flag terminates not only the current round of rewrite processing (like [L]) but also prevents any subsequent rewrite processing
Beware that it should be set on the last rewriting rule that needs to apply if there are several, because no more rewrite will be done after.

Let .htaccess RewriteRule redirect to a script "in current dir" (instead of an explicit path)

I'm using a RewriteRule in .htaccess to redirect anything that is not an existing file, to a "cms.php" file which dynamically handles any request (or outputs some error message if appropriate).
Here's what I do in .htaccess:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /cms.php [L,QSA]
Works like a charm.
However, for development purposes, I want to host the same site on my local XAMPP system as well. On my actual live webserver, the cms.php is right in de document root folder, hence I can use /cms.php there. But on my local development machine, this site is in some subdir, i.e. /cms.php is not the right path.
I also tried "cms.php" (without the / in front of it) as well as "./cms.php" (hoping that "." would denote the current dir) but to no avail. Only when I explicitly specify /the/correct/subdir/cms.php in the RewriteRule, it works OK, but obviously this is only valid on the development machine and not on my live webserver.
Can I somehow use a smart path or mod_rewrite variable or something, so that .htaccess understands it needs to redirect to my cms.php file in the same dir as where .htaccess itself resides ?
Typically, you'd just set a RewriteBase to the dev directory and leave the leading slash off of your rule's target:
RewriteEngine On
RewriteBase /subdir/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ cms.php [L,QSA]
But if that's not good enough, and you want to be able to move the htaccess file around arbitrarily without having to alter the base all the time, ou can try to do something crazy by detecting an arbitrary base:
RewriteCond %{ENV:URI} ^$
RewriteRule ^(.*)$ - [ENV=URI:$1]
RewriteCond %{ENV:BASE} ^$
RewriteCond %{ENV:URI}::%{REQUEST_URI} ^(.*)::(.*?)\1$
RewriteRule ^ - [ENV=BASE:%2]
at the very top of your htaccess file, then using the BASE environtment variable:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ %{ENV:BASE}cms.php [L,QSA]

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)

HTACCESS Mod_Rewrite Troubles

I'm having a bit of trouble with my mod_rewrite, I had this running perfectly fine on my previous litespeed VPS. Now I'm using my own dedicated server running CentOS 6, so I don't know if I haven't configured correctly.
This is how it currently looks:
http://***.com/?pageName=FourthPage
This is how I want it to look:
http://***.com/FourthPage
This is my current .htaccess file:
RewriteEngine On
RewriteCond %{REQUEST_fileNAME} !-d
RewriteCond %{REQUEST_fileNAME} !-f
RewriteRule ^([^/\.]+)/?$ index.php?pageName=$1
I am not good with htaccess syntax. Configuring such rules should be done directly in the server configuration if you have access. Using htaccess for such purposes is only a workaround. However:
The pattern inside the RequestRule is wrong. Especially the trailing slash ('/'). What is it meant to match? You might want to read again through the documentation of that rule. The pattern is not matched against a full URL but only against a part of the path in case of htaccess.
Have a try with the following:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]*)$ index.php?pageName=$1
Also you should enable rewrite logging inside the server. The relevant configuration options are RewriteLog (for the file) and RewriteLogLevel (well, for the level...). It help to understand what is going on inside the rewrite module. However that cannot be done in htaccess too, you need access to the servers general configuration.

Apache .htaccess mod_rewrite and clean urls

Ok. So I'm building this site which is accessible through two different domains. So I can't use RewriteBase in my .htaccess. The rules (below) I use to work around this problem seem to work fine. However, when I use the below .htaccess settings on my local box with clean URLS (WAMP) it all works fine but the moment I use this on the live server (shared hosting LAMP) every page I navigate to displays the home page (the one under index I guess) even though the URL in the browser is clearly being updated.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^/domain1.com/(.*)$
RewriteRule ^(.*)$ /domain1.com/index.php?q=$1 [L,QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^/domain2.com/(.*)$
RewriteRule ^(.*)$ /domain2.com/index.php?q=$1 [L,QSA]
</IfModule>
Any help or ideas are very much appreciated.
Luke
Probably the best thing to do is to reproduce the problem on your local box and turn up RewriteLogLevel so you can see what's going on. (Since you usually can't change the log level on shared hosting)
You may be able to "simulate" the problem by doing a directory rewrite in your Apache main configuration. (The shared hosting obviously does its own rewriting before it gets to the .htaccess!) If you can't reproduce the problem, you may have to start trial-and-error debugging on the remote server. This is ugly but if it's your only option:
Use the R (redirect) flag in substitutions to send any rewritten URL back to your browser. Use TELNET (or an appropriate browser add-on) to inspect the HTTP responses.
Don't forget to escape dots in regexes!
As a side note, the RewriteRule pattern is matched before the RewriteConds above it. This kind of setup is probably better for performance:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/domain1\.com/(.*)$ /domain1.com/index.php?q=$1 [L,QSA]
# ^ should be escaped
Note that I haven't tested this.