It is possible with Apache to host multiple websites with a single static IP address. However, you can only have one SSL certificate per static IP. This post describes setting up Apache with multiple secure virtual hosts and a single self-signed wildcard certificate. To learn about creating the certificate, check out Creating Self-Signed Certs on Apache 2.2.

Here’s a table of our example hosts:

Domain Force SSL? Trusted? Valid Domain?
www.site-a.com No No Yes
secure.site-a.com Yes No Yes
test.site-a.com No No Yes
www.site-b.com No No No
secure.site-b.com Yes No No

In order for the certificate to be trusted, you need to obtain the certificate from a trusted certificate authority. Since we are using self-signed certificates, they are not trusted and we will see some warning messages. The data will still be encrypted.

For this to work, we are going to use name-based virtual hosts.

The name and location of Apache’s configuration files vary based on which platform you use. This post assumes Ubuntu in which case the configuration files in the /etc/apache2 directory.

For starters, we need to tell Apache which ports to listen on by editing the file /etc/apache2/ports.conf.

Listen 80
<IfModule mod_ssl.c>
    Listen 443
</IfModule>

Next we need to add our virtual hosts. They are kept in the /etc/apache2/sites-available directory. For organization purposes, separate your sites into separate config files, then run a2ensite for each site to generate a symbolic link in the /etc/apache2/sites-enabled directory.

Here is the configuration for the sites:

NameVirtualHost 192.168.1.200:80
NameVirtualHost 192.168.1.200:443

# http://site-a.com
# https://site-a.com -- Throws warning because cert is for *.site-a.com... see bottom
# http://www.site-a.com
# https://www.site-a.com
<VirtualHost 192.168.1.200:80 192.168.1.200:443>
  ServerName site-a.com
  ServerAlias www.site-a.com
  DocumentRoot /path/to/www.site-a
  # Note: SSL settings only need to be defined once!
  SSLEngine On
  SSLCertificateFile /path/to/thecert.crt
  SSLCertificateKeyFile /path/to/thecert.key
</VirtualHost>

# Not SSL, redirect to https://secure.site-a.com
<VirtualHost 192.168.1.200:80>
  ServerName secure.site-a.com
  Redirect / https://secure.site-a.com/
</VirtualHost>

# https://secure.site-a.com
<VirtualHost 192.168.1.200:443>
  ServerName secure.site-a.com
  DocumentRoot /path/to/secure.site-a
</VirtualHost>

# http://test.site-a.com
# https://test.site-a.com
<VirtualHost 192.168.1.200:80 192.168.1.200:443>
  ServerName test.site-a.com
  DocumentRoot /path/to/test.site-a
</VirtualHost>

# http://www.site-b.com
# https://www.site-b.com -- Throws warning because cert is for *.site-a.com
<VirtualHost 192.168.1.200:80 192.168.1.200:443>
  ServerName www.site-b.com
  DocumentRoot /path/to/secure.site-b
</VirtualHost>

# Not SSL, redirect to https://secure.site-b.com
<VirtualHost *:80>
  ServerName secure.site-b.com
  Redirect / https://secure.site-b.com/
</VirtualHost>

# https://secure.site-b.com -- Throws warning because cert is for *.site-a.com
<VirtualHost 192.168.1.200:443>
  ServerName secure.site-b.com
  DocumentRoot /path/to/secure.site-b
</VirtualHost>

Despite having a wildcard certificate for *.site-a.com, you will get an invalid domain message when you don’t supply the subdomain (i.e. http://site-a.com). The only way I know of to solve this is with 2 static IPs and 2 certs where one cert is for site-a.com and the other is for *.site-a.com.

I’m using 192.168.1.200 for the server’s IP address behind the firewall. You could try using * instead of 192.168.1.200 in the <VirtualHost> blocks, but I haven’t tested this. Leave a comment if you happen to test this. 🙂

Assuming the stars have aligned, you should have several secured virtual hosts!


I recently had to integrate search capabilities into a website I worked on. I chose to use the Google AJAX Search API. I needed the form field display to be very customized and clean. I do leverage the Dojo Javascript Toolkit to help with the boilerplate DOM and event stuff.

Search Form

The first thing you need to do is sign up for a new key:

Sign-up for an AJAX Search API Key

Once you get the key, you’re ready to start coding your own custom search form.

Begin by adding the the following to your <head> tag:

<script src="http://www.google.com/jsapi?key=<KEY GOES HERE>" type="text/javascript"></script>

Since my example uses Dojo, you need to include Dojo for my code to work. If you don’t already use Dojo, you can simply include it by adding the following to the <head>:

<script src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js" type="text/javascript"></script>

Next we need to add our code that performs the search. First tell the Google API to load the search API:

<script type="text/javascript">
google.load("search", "1", {"nocss":true,"nooldnames":true});

Next we need to initialize our page when the page loads:

dojo.addOnLoad(function(){
    var results = dojo.byId("results");
    var cursor = dojo.byId("cursor");
    var ws = new google.search.WebSearch();
    ws.setNoHtmlGeneration();

Be sure to set the domain you want to search. The LARGE_RESULTSET will return the maximum number of results Google allows, which is usually 8 results.

    ws.setSiteRestriction("example.com");
    ws.setResultSetSize(google.search.Search.LARGE_RESULTSET);

Next we need to tell the WebSearch object the function we want called after the results are returned. The 3rd argument to setSearchCompleteCallback() is an array with a reference to our WebSearch instance. This array gets passed to our callback function when the callback is fired.

    ws.setSearchCompleteCallback(null, function(w){
        if(!w.results){return;}
        results.innerHTML = "";
        cursor.innerHTML = "";
        var d = dojo.doc.createElement("div");
        results.appendChild(d);
        for(var i=0,len=w.results.length; i&lt;len; i++){
            var r = w.results[i];

Our WebSearch object has a createResultHtml() function that creates an the DOM nodes of our result, but it adds a bunch of crap and forces the result to open in a new window. So, to fix that, we use Dojo to go clean up the nodes before adding them to the page.

            w.createResultHtml(r);
            if(r.html){
                var n = r.html.cloneNode(true);
                dojo.query(".gs-visibleUrl", n).forEach(function(p){
                    p.parentNode.removeChild(p);
                });
                dojo.query(".gs-watermark", n).forEach(function(p){
                    p.parentNode.removeChild(p);
                });
                dojo.query("a", n).forEach(function(p){
                    if(p.getAttribute("target")){
                        p.removeAttribute("target");
                    }
                });
                results.appendChild(n);
            }
        }
        if(w.cursor){
            var cn = dojo.doc.createElement("div");
            cn.className = "gsc-cursor";
            var label = dojo.doc.createElement("div");
            label.innerHTML = "Pages:";
            label.className = "label";
            cn.appendChild(label);
            for(var i=0, len=w.cursor.pages.length; i&lt;len; i++){
                var pn = dojo.doc.createElement("div");
                pn.className = "gsc-cursor-page";
                if(i == w.cursor.currentPageIndex){
                    pn.className += " gsc-cursor-current-page";
                }
                pn.innerHTML = w.cursor.pages[i].label;
                pn.idx = i;
                dojo.connect(pn, "onclick", function(evt){
                    ws.gotoPage(evt.target.idx);
                });
                cn.appendChild(pn);
            }
            cursor.appendChild(cn);
        }
    }, [ws]);

If you want to execute a search when the page loads, then you can fire execute() with the criteria.

    ws.execute("");

Finally we need to wire up the search button and form. We don’t care about the event details, we just want to execute the search for the current value in the search criteria field.

    function query(evt){
        dojo.stopEvent(evt);
        ws.execute(dojo.trim(dojo.byId("searchCriteria").value));
    }
    dojo.connect(dojo.byId("searchButton"), "onclick", query);
    dojo.connect(dojo.byId("searchForm"), "onsubmit", query);
});
</script>

In the body of your page, add the following markup to define the form:

<div>
    <form id="searchForm">
        <input type="text" autocomplete="off" id="searchCriteria" value=""/>
        <a href="#" id="searchButton"><span>Search</span></a>
    </form>
</div>
<div id="results"></div>
<div id="cursor"></div>

For clarity, I removed all styling. One thing you’ll notice is the “Search” button is an <a> tag instead of an <input> tag so that it could be more easily be styled.

Aside from our text field and search button, there are some CSS styles you can tweak for the search results and pagination:

<style type="text/css">
.gs-result{padding:6px 0;}
.gsc-cursor{display:inline;padding:10px 0;}
.gsc-cursor .label{color:#000;display:inline;font-weight:bold;margin-right:8px;}
.gsc-cursor-page{cursor:pointer;color:#00F;display:inline;margin-right:8px;
text-decoration:underline;}
.gsc-cursor-current-page{color:#000;font-weight:bold;text-decoration:none;}
</style>

That’s all there is to it. Load the page up and do a search!

Search Results