Can you use Apache's <If> to conditionally set the Document Root (without using mod_rewrite)? - apache

I need to set Apache's DocumentRoot directive inside a <VirtualHost> according to whether a particular remote IP address is calling it. I could use mod_rewrite for this but once I've "made the switch" I subsequently have a very complicated .htaccess file in the two destination folders themselves. I figure I need to do this inside httpd.conf. Is there a way to conditionally switch the entire Virtual Host directory based on my IP?
I am close with the following code, but it doesn't change based on my IP.
<VirtualHost _default_:443>
<If "-R '1.2.3.4'">
Define mydocumentroot "/var/www/just_for_me"
</If>
<Else>
Define mydocumentroot "/var/www/everyone_else"
</Else>
DocumentRoot ${mydocumentroot}
</VirtualHost>
The DocumentRoot variable correctly gets set to the mydocumentroot variable, but this always evaluates to the <Else> clause, no matter what I try.
Thanks!

After endless experiments and research it seems that doing any kind of switching in httpd.conf or .htaccess is slow and unnecessarily taxing for the server. I ended up working it out using a combination of mod_rewrite and soft links.

Related

Apache VirtualHost conditional based on an environment variable

I'm trying to include a config file inside an Apache 2.4 <VirtualHost> based on the presence of an environment variable.
Inside the VirtualHost declaration, I set the VIEWMODE environment variable as such:
Define virtualhost_config "${virtualhost_path}/conf/virtualhost.conf"
<VirtualHost *:80>
SetEnv VIEWMODE demo
Include "${virtualhost_config}"
</VirtualHost>
Inside the included config file, I now have this conditional inside the <Directory> block:
<If "env('VIEWMODE') == 'demo'">
RewriteRule (.*) http://www.apple.com/ [L,R=302]
</If>
However, I can't seem to get this to work. The conditional RewriteRule is ignored.
What am I missing?
See the documentation for SetEnv:
The internal environment variables set by this directive are set after most early request processing directives are run, such as access control and URI-to-filename mapping. If the environment variable you're setting is meant as input into this early phase of processing such as the RewriteRule directive, you should instead set the environment variable with SetEnvIf.
And the note about environment variables within the functions section of the Apache Expresions documentation is also of interest:
Environment variable ordering
When environment variables are looked up within an <If> condition, it's important to consider how extremely early in request processing that this resolution occurs. As a guideline, any directive defined outside of virtual host context (directory, location, htaccess) is not likely to have yet had a chance to execute. SetEnvIf in virtual host scope is one directive that runs prior to this resolution
When reqenv is used outside of <If>, the resolution will generally occur later, but the exact timing depends on the directive the expression has been used within.
So you need to use SetEnvIf because SetEnv is not processed soon enough, and that fixed it for me when I tested here. Something like:
Define virtualhost_config "${virtualhost_path}/conf/virtualhost.conf"
<VirtualHost *:80>
SetEnvIf Request_URI ^ VIEWMODE=demo
Include "${virtualhost_config}"
</VirtualHost>
I solved this by setting an Apache variable, which then fed both the SetEnv directive as well as the <If> block. Unfortunately, the <If> block itself seemed to cause issues with the processing order of directives inside it (e.g. ServerAlias not allowed here), but <IfDefine> did not have this problem (using <IfDefine> only worked for me because VIEWMODE was binary). The final solution looked something like this:
Define environment production
Define viewmode demo
<VirtualHost *:80>
SetEnv ENVIRONMENT ${environment}
<IfDefine viewmode>
SetEnv VIEWMODE ${viewmode}
Include "${virtualhost_path}/conf/demo-configuration.conf"
</IfDefine>
</VirtualHost>
UnDefine environment
UnDefine viewmode
An important caveat is that Apache variables are global, so if the same variables might be used in subsequent Virtual Hosts, make sure to UnDefine them at the end of each config.

Name based virtual host?

I have a new site that uses Apache2. Looking at my server logs I see that there is a growing bunch of other domain names (all hosted with GoDaddy) that are mysteriously pointing to the IP address for my server. I do not know that this does any harm but I cannot see any reason why I should allow the situation to persist. From the bit of Googling I have done I have gathered that the way to stop this is to set up a name based virtual host and reject any attempts by other domain names to point to my content. However, I am not at all sure how I set up the relevant entries. Looking at commentary in my /etc/apache2/apache2.conf file I get the impression that I should do something along the lines of
NameVirtualHost mysite.com:80
<VirtualHost mysite.com:80>
ServerName mysite.com
ServerAlias:www.mysite.com
ErrorLog:/var/log/mysite.error.log
CustomLog:/var/log/mysite.access.log
</VirtualHost>
This may or may not be right but I have never tinkered with apache configuration files in the wild and that apart it is not at all clear to me that this will stop anothercrappysite.com pointing to my IP address and displaying my content.
I'd be much obliged to anyone who might be able to tell me how I do things right here.
p.s. - from what I can see I do not appear to have an httpd.conf file. Just apache2.conf.
There is perhaps another way to do this by writing an htaccess rule that specifically redirects all requests that do not contain mysite.com or www.mysite.com to the default 404 page?
For the benefit of anyone who comes here in quest of a solution to a similar issue here is what to do
<VirtualHost ip_address:80>
ServerAdmin admin#...
DocumentRoot /var/www/html/
ServerName mysite.com
ServerAlias www.mysite.com
ErrorLog /var/log/mysite.error.log
CustomLog /var/log/mysite.access.log combined
RewriteEngine On
RewriteCond %{HTTP_HOST} !^(www.mysite|mysite).com$ [NC]
RewriteRule ^ [F]
</VirtualHost>
It goes without saying that you should
Put in the IP address of your server
Use the domain name of your site
Ensure that mod_rewrite is enabled
You can check to see what modules are enabled via a simple PHP script
<?php
print_r(apache_get_modules());
?>
If mod_rewrite does not figure in that list you can usually get it by simply copying it from
/etc/apache2/mods-available to
/etc/apache2/modes-enabled
and then restarting Apache
service apache2 restart

Apache: <Directory> directive with no path specified

I have a local server environment running on my OS X machine using MAMP, and I am currently setting up a few virtual hosts. I found that, in order for one of my virtual hosts to recognize my site's .htaccess file, I needed to add a <Directory> directive within the <VirtualHost> directive.
Here is an example of what I am using to configure one of my virtual hosts:
<VirtualHost *>
DocumentRoot "/path/to/mysite"
ServerName mysite.local
<Directory "/path/to/mysite">
AllowOverride All
Order Allow,Deny
Allow from all
</Directory>
</VirtualHost>
Now, I would like to avoid redundancy by removing the path from that <Directory> directive. I tried doing so and it seems to work, although I am not familiar with Apache enough to know the potential consequences of doing this. Furthermore, I could not find an explanation of this case in the Apache Documentation.
This is what I would like to do:
<VirtualHost *>
DocumentRoot "/path/to/mysite"
ServerName mysite.local
<Directory>
AllowOverride All
Order Allow,Deny
Allow from all
</Directory>
</VirtualHost>
Therefore, my question is this: what happens when I omit the path from the <Directory> directive (in this case)?
Directory is a directive targeting a file system directory, and should always have a path (waildcards allowed).
Using some instructions targetd to no directory may be discarded silently, or maybe with an error (you could test it), but is would certainly be useless.
Why would you write some configuration instructions targeted to no directory at all?
But there's maybe more things in your question.
Ususally a Virtualhost should contains at least one Directory instruction, the DocumentRoot directory, and usually we also add one targeting the root of the filesystem, this way:
<Directory />
(instructions to deny access and avoid .htaccess reads)
</Directory>
<Directory /my/document/root/>
(instructions to allow access and still avoid .htaccess reads or to allow them if you are lazy and feel over confident that .htaccess are useful in any way and you do not need speed)
</Directory>
And you could add more, especially to avoid usage of .htaccess files as such file stored in /path/to/somewhere is the same thing as a <Directory /path/to/somewhere> section, but slower.
Now you do not like to repeat yourself in your VH configuration, so you coud maybe use .htaccess files or global level instructions for things that are usually set there. And you could also use another magical keyword, which is Location.
You can use a lot of Apache instruction in a <Location /foo> directive. Locations works on url, not on filesystem paths.
So <Location /> is your documentRoot, and <Location /foo> is maybe the subdirectory 'foo' under you DocumentRoot. This could help you write things in your VH without reusing the documentroot prefix everywhere. But be careful, urls are not file-system paths. Any protection applied on the url level may be weaker or stronger compared to a protection applied on a directory level. It depends on your alias, the way you use urls, etc.
Update:
If you use the new Apache 2.4 version you can now use mod_macro or even easier, built-in variables support:
Define docroot "/path/to/mysite"
DocumentRoot ${docroot}
<Directory ${docroot}>
(...)

Single htaccess file with distinction between production and development

I am currently using two .htaccess files for my website: one is the development version, and the other one is the production version. The difference is in absolute and some relative paths that are used for RedirectMatch, RewriteRule, and ErrorDocument directives, and that differ between development and production. I am using a git hook to remove the dev version and rename the production version, but this still means I have to maintain two versions of basically the same file. Also, I am encountering similar problems with other sites I manage, so this all means more work. I rather have one single file and some sort of condition in the .htaccess file that allows me to say if production -> use these, else -> use these. Can I do such thing in an .htaccess file?
I have found this:
.htaccess between developemt, staging, and production
But this is for rewrite rules and would not fix the different ErrorDocument paths.
If your servers are running Apache 2.3 or higher, you can use an <if expr> directive like this:
<if "%{HTTP_HOST} == 'dev.example.com'">
# dev directives
</if>
<if "%{HTTP_HOST} == 'www.example.com'">
# prod directives
</if>
Earlier versions of Apache will throw a 500 Server Error if you use <if>, be sure to check the Apache version before trying this.
I ran into an issue where I needed a similar solution but could not use <if expr> directive because I was running Apache 2.2
An alternative is to put password protect instruction in your apache config (vhost-conf in my case) file within the tag like below. Note that you still need a .hpasswd file
ServerAdmin xxx#lami.me
DocumentRoot "/doc/root/html"
ServerName dev.lami.me
ServerAlias dev.lami.me localhost
ErrorLog "/doc/root/logs/error_log"
CustomLog "/doc/root/logs/access_log" common
<Directory "/doc/root/html">
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
Allow from all
AuthName "Restricted Area"
AuthType Basic
AuthUserFile /doc/root/.htpasswd
AuthGroupFile /dev/null
require valid-user
</Directory>
I rather have one single file and some sort of condition in the .htaccess file that allows me to say if production -> use these, else -> use these. Can I do such thing in an .htaccess file?
No. Sorry.
However you may construct your .htaccess to work on both with a bit of care. And I have done this for my test and prod systems.
IMO, the easiest way to do this is to make your development site mirror your production site in some key aspects and this is what I do. For example if your prod domain is mydomain.com and your test environment is on localhost, the add an alias of mydomain.home for localhost in your etc/hosts file. You can now add a vhost for mydomain.home and mirror the document roots and environment setting of prod in this. If you need to refer to the HTTP_HOST, you can now use mydomain.(com|home) and use %1 to distinguish which.
This assumes that you are running your dev env on a LAMP system. If not it's pretty easy to set up a VM to do this (as I describe here)
You can use RewriteRules instead of RedirectMatch directives and with Rewriterules you can use the [C],[OR],[E] and [S] flags to implement branch-style ladder-logic which can effectively mirror if/then/else constructs.
The main gotcha is DocumentRoot setting if you production is on a shared hosting environment and with impacts local ErrorDocument paths, but without knowing you environments, it is difficult to give more specifics

Apache rewrite domain requests to subdomain requests

Caveat: I am not an Apache expert or webmaster by training or trade (C++ developer), so I expect this is a fairly obvious, newbie-level question. Apologies in advance.
I need an Apache 2.x rewrite rule that will map a requested domain into our domain as a subdomain.
Simplified Example(s):
domain1.com/index.php?option=80 -> domain1.masterdomain.com/index.php?option=80
www.domain1.com/index.php?option=99 -> domain1.masterdomain.com/index.php?option=99
domain2.com/index.php?option=33 -> domain2.masterdomain.com/index.php?option=33
www.domain2.com/index.php?option=44 -> domain2.masterdomain.com/index.php?option=44
I've tried a variety of suggested options, but so far, no joy. The latest attempt is:
RewriteRule ([^.]+)\.com(.*) http://$1.masterdomain.com [L]
Note: this lives in a virtual host that owns port 80 on a specific IP, so nothing else interesting going on in the VHost that I can see having any affect on this.
I believe my problem is all in my regex, but honestly, it's eluding me.
Any assistance would be much apperciated. I've been studying the Apache docs and all the Googled tips I can find, but I'm just not seeing it.
Thanks~
Actually, there's no need to rewrite the domain name, as long as the "source" and "destination" domains are handled by the same server. You just need to alias the different domains to refer to the same host. Specifically: I suppose that right now you have <VirtualHost> blocks that look something like this:
<VirtualHost *:80>
ServerName domain1.masterdomain.com
# other rules
</VirtualHost>
<VirtualHost *:80>
ServerName domain2.masterdomain.com
# other rules
</VirtualHost>
All you have to do is add the ServerAlias directives as shown below:
<VirtualHost *:80>
ServerName domain1.masterdomain.com
ServerAlias domain1.com
ServerAlias www.domain1.com
# same other rules as before
</VirtualHost>
<VirtualHost *:80>
ServerName domain2.masterdomain.com
ServerAlias domain2.com
ServerAlias www.domain2.com
# same other rules as before
</VirtualHost>
This will make Apache handle the domain names domain1.com and www.domain1.com the same way it handles domain1.masterdomain.com.
Thanks for the fast reply! Actually, I have a single VHost that's handling all the sites (since they all have the same structure), leveraging VirtualDocumentRoot directives. I can post the whole VHost entry if desired, but I thought that would be overkill for my question.
I have made progress since my previous post. The following executes the rewrite as desired, albeit it updates the browser url, and I'm looking to NOT have the user know they've been redirected internally.
RewriteCond %{HTTP_HOST} ^(www\.)?([^.]+)\.com$ [NC]
RewriteRule ^(.*)$ http://%2.masterdomain.com$1
I'm not sure why it's updating the browser URL on it's redirect though. I thought I read that only happens if you explicitly add a [R] on the end of the rule line. Ultimately, the browser needs to still display: test1.com/index.php?id=5, while internally the cgi receives test1.masterdomain.com/index.php?id=5.
Another data point that may be helpful (or not), I see in my rewrite.log that, after the match, it's implicitly executing a redirect (rc=302). Here's the two lines I think are important:
implicitly forcing redirect (rc=302) with http://test1.masterdomain.com/index.php
redirect to http://test1.masterdomain.com/index.php?id=5 [REDIRECT/302]
Any thoughts/suggestions are greatly appreciated! 20+ years C++, but just under a week with Apache, so still a very struggling newbie.
-wb
P.S. due to some coding requirements for the web apps that are locked, I actually need the url received by the cgi to be xxx.masterdomain.com (long story, not my choice or approach; just trying to solve the problem I've been given with it's highly constrained requirements.... Also, my apologies for the sanitized data-- following directives from on-high. -smile-) Thanks again!