11

Is it possible to configure Apache for different virtual hosts based on the source IP? (i.e. same interface, same hostname, but two different virtual hosts, with different content, based on source IP.)

The motivation for this is so that my IP address can access the site proper, but that everyone else gets the holding page. The conventional solution seems to be to use mod_rewrite to direct visitors to a separate page within the same docroot, but I'd like to use a completely different docroot for the holding page instead.

8
  • BTW, why do you need this logic for yourself on the server? You can map the domain to the IP of a testing server (or your own computer) for just yourself using your hosts file. Aug 2, 2011 at 10:47
  • do you have several names to handle or only one? as you can work (define the Virtualhosts) with names Or with IP.
    – regilero
    Aug 2, 2011 at 11:28
  • any particular reason why a different doc root? Aug 2, 2011 at 11:36
  • The OP's stated rationale doesn't make sense (a holding page works great with a rewrite), but if you were, say, reworking a site, needed a way to host the rework, and didn't know that subdomains existed, you might decide to do it this way.
    – womble
    Aug 2, 2011 at 11:45
  • You could use Allow/Deny lines to block access to everyone except you and have a custom error document for 403 errors (Make sure you put it in a directory and allow access to that directory, else the error document will 403 as well), or use rewrite rules/rewrite conditions, or move your pre-launch/testing site to a subdomain like dev.example.com then example.com could have the holding page
    – user80776
    Aug 2, 2011 at 11:46

5 Answers 5

5

I don't know if that's possible (without mod_rewrite, anyway) in Apache level.

Here's another idea. What if you set up two Apache virtual hosts and then use iptables to transparently forward visitor to correct virtual host? Something like

iptables -A PREROUTING -t nat -i eth0 -p tcp -s your.ip.address -d your.server --dport 80 -j DNAT --to-destination your.actual.site:someport
iptables -A PREROUTING -t nat -i eth0 -p tcp ! -s your.ip.address -d your.server --dport 80 -j DNAT --to-destination your.holding.site:someport

Or something similar. :)

1
  • I'd say this is going to be the least-worst option.
    – womble
    Aug 2, 2011 at 11:46
8

It wouldn't really be a different virtual host. But using something like mod_rewrite or mod_alias you can serve content out of any folder for which you have set the appropriate permissions. There's only one docroot, but you can effectively change that on the fly.

One way to do it might be:

<VirtualHost *.80>
    ServerName example.com
    ...
    DocumentRoot "/path/to/root"
    <Directory "/path/to/root">
       ...
    </Directory>
    <Directory "/path/to/not/root">
       Order allow,deny
       #replace with your IP
       Allow from 192.168.0.100 
       ...
    </Directory>
    RewriteEngine On
    #Rewrite to alternate path if IP address matches
    RewriteCond %{REMOTE_ADDR} ^192\.168\.0\.100$
    RewriteRule ^/(.*)$ /path/to/not/root/$1
<VirtualHost>

Do note though that it'd probably be a bit cleaner to handle this with a dev subdomain.

6

Apache 2.3 or later

With Apache 2.3 or later you can apparently do something like this (tested):

<VirtualHost *:80>
    ServerName www.example.com

    <If "-R '10.10.10.10'">
        # The next version of the website...
        Alias /favicon.ico /home/ubuntu/website-new/favicon.ico
        Alias /static/ /home/ubuntu/static/
        WSGIScriptAlias / /home/ubuntu/website-new/main/wsgi.py
    </If>
    <Else>
        # The standard version (e.g. holding page).
        Alias /favicon.ico /home/ubuntu/website/favicon.ico
        Alias /static/ /home/ubuntu/static/
        WSGIScriptAlias / /home/ubuntu/website/main/wsgi.py
    </Else>

    # and so on...

</VirtualHost>

Apache 2.2 or earlier

Update: This is not a good solution. See below.

You have to do a hack like this. Note the [PT] which stands for "passthrough". Without it, an actual HTTP redirect is sent back to the client, which probably isn't what you want. The [OR] thing (which stands for "or") shows how to match multiple addresses.

Alias /next/favicon.ico /home/ubuntu/website-new/favicon.ico
Alias /next/static/ /home/ubuntu/static/
WSGIScriptAlias /next /home/ubuntu/website-new/main/wsgi.py

Alias /favicon.ico /home/ubuntu/website/favicon.ico
Alias /static/ /home/ubuntu/static/
WSGIScriptAlias / /home/ubuntu/website/main/wsgi.py

# Rewrite for our IP.
RewriteEngine On
RewriteCond %{REMOTE_ADDR} ^80\.4\.170\.209$ [OR]
RewriteCond %{REMOTE_ADDR} ^94\.193\.52\.157$
RewriteRule ^/(.*) /next/$1 [PT]

You need to enable mod_rewrite which you can do on Debian/Ubuntu with this command:

sudo a2enmod rewrite

Note that this method doesn't completely ban other people from accessing your test site, so you will probably want to add some security, or just choose a more obscure prefix than next.

Update on the mod_rewrite method.

There are a couple of problems with this method. First, Django does not work with two sites in the same process like this, you need to follow the instructions in this answer.

Secondly mod_rewrite does not work with POST requests! All POSTs are silently changed to GET and the post data is discarded. Very frustrating! Therefore I recommend you use the...

iptables version

Simply run the servers on two different ports. This one includes the WSGI stuff to have two separate django sites.

<VirtualHost *:80>
    ServerName www.example.com

    Alias /favicon.ico /home/ubuntu/alpha/favicon.ico
    Alias /static/ /home/ubuntu/alpha/static/

    WSGIDaemonProcess alpha_wsgi user=www-data group=www-data
    WSGIScriptAlias / /home/ubuntu/alpha/alpha/wsgi.py
    WSGIProcessGroup alpha_wsgi

    ServerAdmin [email protected]

    ErrorLog ${APACHE_LOG_DIR}/error.log

    # Possible values include: debug, info, notice, warn, error, crit, alert, emerg.
    LogLevel warn

    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost *:1222>
    ServerName www.example.com

    Alias /favicon.ico /home/ubuntu/main/favicon.ico
    Alias /static/ /home/ubuntu/main/static/

    WSGIDaemonProcess main_wsgi user=www-data group=www-data
    WSGIScriptAlias / /home/ubuntu/main/main/wsgi.py
    WSGIProcessGroup main_wsgi

    ServerAdmin [email protected]

    ErrorLog ${APACHE_LOG_DIR}/error.log

    # Possible values include: debug, info, notice, warn, error, crit, alert, emerg.
    LogLevel warn

    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Then you can use this iptables command to route requests from your ip address on port 80 to port 1222:

sudo iptables -A PREROUTING -t nat -p tcp -s your.ip.address --dport 80 -j DNAT --to-destination :1222

Change -A to -D to remove the rule.

Note that the docs suggest you need to add additional Listen and NameVirtualHost commands, but I actually found that it works without them, and adding them made it break (at least in ubuntu).

1
  • Your answer for 2.3+ gives Alias cannot occur within <Directory/Location/Files> section any fix? I really need this :$
    – Gizmo
    Jan 11, 2018 at 21:18
2

AFAIK, the only way to do this is to symlink a location within the document root to your content outside the document root, then rewrite the request to that.

1

Like @Timmmm said, but correcting the ipmatch statement (note the '10.10.10.10'): ServerName www.example.com

<If "%{REMOTE_ADDR} -ipmatch '10.10.10.10'">
    # The next version of the website...
    Alias /favicon.ico /home/ubuntu/website-new/favicon.ico
    Alias /static/ /home/ubuntu/static/
    WSGIScriptAlias / /home/ubuntu/website-new/main/wsgi.py
</If>
<Else>
    # The standard version (e.g. holding page).
    Alias /favicon.ico /home/ubuntu/website/favicon.ico
    Alias /static/ /home/ubuntu/static/
    WSGIScriptAlias / /home/ubuntu/website/main/wsgi.py
</Else>

# and so on...

Because otherwise it will show the error:

Cannot parse condition clause: -ipmatch requires subnet/netmask as constant argument

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .