Ask a Jedi: Building a ColdFusion site to support dynamic subdomains

This post is more than 2 years old.

Rick asks:

I am trying to do something that I thought would be simple (ever heard that before). I can not find anywhere that explains how to do it. I am creating a membership website and want to put the member name in front of the domain name. Such as member.site.com Is there a way to do this programatically? If it is a IIS hack is there a way to add this and take it away on the fly?

I wasn't quite sure what Rick meant at first, but after some clarification, I realized he wanted something similar to how RIAForge works. If you visit multiple different projects at RIAForge, you will notice that each project is a subdomain: blogcfc.riaforge.org, lighthousepro.riaforge.org, makebeerwithcf.riaforge.org. I'm not creating a new web site for each project. (Although in theory I could. Apache uses a text file for configuration and IIS is supposedly scriptable as well.) I use one code base that dynamically responds to the current subdomain. Here is a very simple version of how I did it at RIAForge.

First off, let's talk server and domain configuration. This is Mac/Apache specific, but it shouldn't be that different for IIS or Windows. I created the following site definition in Apache. Notice the server name and 2 aliases.

<VirtualHost *:80> ServerName dev.cowbell.com ServerAlias *.cowbell.com DocumentRoot "/Library/WebServer/cowbell" Options Indexes MultiViews <Directory "/Library/WebServer/cowbell"> Options Indexes Includes MultiViews FollowSymLinks AllowOverride All Order Allow,Deny Allow from All </Directory> </VirtualHost>

You can ignore pretty much everything after the first three lines. The ServerName gives the main domain name for the site. The ServerAlias is an alias. Notice the use of the wildcard (*). This means I can do dfkjfkjfdjfdkjd.cowbell.com and the server will respond to it.

Save this, reload Apache, and ensure you didn't type anything (obviously fix the filepaths to something that makes sense on your system).

Now, unless you run your own DNS, you can't do wildcard DNS to your own machine. (Although I'm no DNS expert, so please correct me if I'm wrong.) I almost always make use of the HOSTS file (which exists on both a Mac and a Windows machine) to specify domain names I want to resolve to my local machine. I added the 3 following lines to my HOSTS file:

127.0.0.1 dev.cowbell.com 127.0.0.1 xxx.cowbell.com 127.0.0.1 yyy.cowbell.com

xxx and yyy are the sample subdomains I want to use. dev represents the 'default' or www version of the domain name.

Save your HOSTS file and quickly add a simple index.cfm in the folder you defined as the site root in Apache. Ensure that all three domains resolve and show you that index.cfm file.

Alright, so now for the fun part. We need to write code that will notice when we are on a subdomain and respond to it. I created a simple Application.cfc and used the following onRequestStart:

<!--- Run before the request is processed ---> <cffunction name="onRequestStart" returnType="boolean" output="true"> <cfargument name="thePage" type="string" required="true"> <cfset var subDomain = ""> <cfset var thisServer = cgi.server_name>
&lt;!--- is it just cowbell.com? ---&gt;
&lt;cfif listLen(thisServer,".") lt 3&gt;
	&lt;cfreturn true&gt;
&lt;/cfif&gt;

&lt;cfset subDomain = listFirst(thisServer,".")&gt;
&lt;!--- If the subdomain is dev, or www, its a root request ---&gt;
&lt;cfif subDomain is "www" or subDomain is "dev"&gt;
	&lt;cfreturn true&gt;
&lt;/cfif&gt;

&lt;cfset request.subDomain = subDomain&gt;
			
&lt;cfreturn true&gt;

</cffunction>

Line by line, this is what the code does. First, it copies the value of CGI.server_name. This CGI variable represents the requested server. I treat this value like a list. If the length of the list is two, then it is a request for cowbell.com. I.e., no subdomain, no www, etc. The return true then simply ends any more logic in the method. If we havne't left the method, I pop off the subDomain using listFirst. If the subdomain is dev or www, then we treat it as a 'normal' request and also leave the method. Finally, if we are still in the method, it means we have a subdomain request.

At this point, what you do is entirely up to the application. At RIAForge, I modify the URL scope to set the event value manually to something like: event=project.homepage&project=SUBDOMAIN. At this point it acts like any normal Model-Glue request.

For my demo, I simply set a request variable. My index.cfm file was modified to reflect this change:

<cfif not structKeyExists(request, "subDomain")>
&lt;h2&gt;Welcome to Cowbell.com&lt;/h2&gt;

<cfelse>

&lt;cfoutput&gt;&lt;h2&gt;Welcome to #request.subDomain#'s Page at Cowbell.com&lt;/h2&gt;&lt;/cfoutput&gt;

</cfif>

As you can see, when the request variable exists, we treat it like a 'user' page. I can now open dev.cowbell.com and see the default home page. Then I can try both xxx and yyy to see the 'personalized' user home pages. If I added zzz.cowbell.com to my HOSTS file, it would work with that as well.

Hope this helps, Rick!

Edit at 9:10 PM Some of the comments made it clear that I may not have made it obvious what I edited my HOSTS file. In this blog entry I'm working locally. The HOSTS file lets me say, on my machine, that domain X (xxx.cowbell.com) points to IP y (127.0.0.1 means my mchine). You would do this when testing your site locally. In production you wouldn't do this normally. You would set up the wildcard DNS with your domain name provider.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Freelance Web Developer posted on 6/2/2009 at 5:46 AM

While working on an RFQ, I had asked my ISP if they'd allow wildcard subdomains. Normally their shared hosting accounts come with only three, and they are directory based (the dir name is the subdomain name). But I explained the site requirements, pretty much the same as riaforge and deviantart, and they were ready to let me do so.
With that, no need for hosts file or server config edits needed.
So, first check with your ISP (if it's not you) - there might be a lot less work for you to do.

Comment 2 by Raymond Camden posted on 6/2/2009 at 5:48 AM

Wow, so in case it wasn't clear, you absolutely would NOT need to do the HOSTS thing for a production environment. Was that not clear? If not, I can add a little note to the end (some folks don't read the comments).

Comment 3 by Michael van Leest posted on 6/2/2009 at 6:08 AM

It did seem to be a post for "production" and not local development...

Comment 4 by Raymond Camden posted on 6/2/2009 at 6:10 AM

Well it is meant for _both_. :) But, let me do a quick edit to the end and you guys can tell me if it makes it better.

Comment 5 by Raymond Camden posted on 6/2/2009 at 6:12 AM

Please read the new comment at the end of the entry.

Comment 6 by Hal Helms posted on 6/2/2009 at 9:26 AM

Loved the post, Ray, but felt it needed a little more...cowbell.

Comment 7 by Terence posted on 6/2/2009 at 11:34 AM

For IIS on Windows, you just specify a host header value. In the example, use cowbell.com as the host header value and it works.

Here's step-by-step:
1) Start IIS Manager
2) Right-click on website (e.g. Default Website) and choose Properties.
3) On the Web Site tab, click on Advanced next to IP address.
4) On the Advanced Web Site Identification screen, find Multiple Identities for this Web Site and click the Add button.
5) Choose the IP address that you use for the site and enter the TCP port. The typical default is (All Unassigned) and port 80.
6) Enter the domain name (e.g. cowbell.com) in the host header value.

Comment 8 by dickbob posted on 6/2/2009 at 12:26 PM

Has anyone got any advice on how to achieve wildcard CNAME DNS. I've asked GoDaddy who manage my DNS and they say no.

Comment 9 by Tom Chiverton posted on 6/2/2009 at 2:32 PM

@dickbob - change DNS provider. I use Gandi, for instance, and you can do anything you like. It's your DNS after all :-)

Comment 10 by dickbob posted on 6/2/2009 at 2:49 PM

@Tom: Thanks for that. Never heard of Gandi.net I guess you're happy to bet the firm on them? Might give them a try although I'm not keen on paying transfer-in fees. Do they want my business or not!

Comment 11 by Sean Coyne posted on 6/2/2009 at 3:49 PM

The most important question is where do I get makebeerwithcf? the link to RiaForge seems broken :)

Comment 12 by Brandon posted on 6/2/2009 at 4:01 PM

@dickbob http://www.dyndns.com/ is what we are using for http://www.spatialkey.com and we have nearly 1000 sites using *.spatialkey.com with different namees.

Comment 13 by Joshua Cyr posted on 6/2/2009 at 4:16 PM

Godaddy does indeed support wildcard domains. I just use * for it.

If doing this with IIS you don't need host headers. Just have a unique IP pointing to the site in question, IIS will then send all sub domains (or anything) that points to that IP to the site. Presto.

One thing to note when doing this is that google and the like will see each site as unique and index each one often each day. If you have a system set up where you have say, 500,000 possible subdomains and a main directory that points people to them (think dmoz type) then you are going to get a LOT of traffic just in search engines. I have found that using caching techniques is often required.

Comment 14 by Rick Jensen posted on 6/2/2009 at 5:41 PM

Thanks for the help with this one Ray!

Comment 15 by dickbob posted on 6/2/2009 at 6:04 PM

@Brandon: Thanks for that. I hadn't looked at dyndns.com for ages and didn't realise that offered all those services now. Bit pricey to use on all 60+ domains I manage but could be useful to try on the wildcard sub domain requirement.

@Joshua: That was the first thing I tried for the CNAME in the GoDaddy Total DNS Control Panel and it gagged on it. Do you have another method?

Comment 16 by Joshua Cyr posted on 6/2/2009 at 6:08 PM

Sorry, didn't notice the CNAME part.

Set @ as A Record and your IP
set * as A record and your IP
set cname www to @

That is how I have em set up and works fine.

Comment 17 by todd posted on 6/2/2009 at 6:59 PM

LOTS of people will put "www" in front of everything. So you might want to modify your algorithm to accept

www.hostname.domain.com
in addition to
hostname.domain.com

to avoid going somewhere you don't expect.

Comment 18 by Raymond Camden posted on 6/2/2009 at 7:02 PM

Didn't I mean www? Or do you mean www.NAME.domain.com? So to support www.blogcfc.riaforge.org?

If so, that's a smart idea.

Comment 19 by Matthew Williams posted on 6/2/2009 at 7:17 PM

I really, really hope the OP doesn't need to use wild card domains along with SSL on these member pages (although, you probably should if there's any kind of login involved) in IIS. If you have a unique IP for the DNS entry, you're golden. If, however, that IP is not unique and you're required to use host headers to sort it all out, you may need to go mucking about in the IIS metabase. I blogged about doing this a goodly ways back here: http://www.geodesicgrafx.co.... Heaven help you if that's actually required though ;).

Comment 20 by anthony posted on 6/2/2009 at 11:51 PM

You should add a case that redirects direct ip access. If I go to 127.0.0.1, you code will think that I'm on the 127 subdomain. Combine that issue with Todd's case of people appending www to everything, it almost seems like it would be easier to just remove the base domain to find the subdomain. Something like, rereplace(cgi.server_name,'\.cowbell\.com$','','ALL')

Comment 21 by Jon Briccetti posted on 6/5/2009 at 11:22 PM

@Ray - my understanding of this question was how to dynamcially create this subdomain using coldfusion - like when you signup for a trial account at basecamp - you get to "pick" your subdomain name when you signup. somehow their ruby? app calls out and makes the DNS entry to create the subdomain, copies the site files into the webroot and kicks you over to the site... conveniently, im looking for someone who's done this, so maybe you know (hint hint)

Comment 22 by Raymond Camden posted on 6/5/2009 at 11:23 PM

@Jon: Yeah, but why would you call out to DNS? We don't do that at RIAForge. Since *.riaforge.org goes to one box, there is no need.

Also, we don't create new CFM files for every project. That would be a waste. Rather - the code notices and responds to the subdomain (see the example above).

Comment 23 by Pablo Varando posted on 6/6/2009 at 2:04 AM

I do this on EasyCFM for the tutorials, and my DNS is done through GoDaddy.

dickbob, On GoDaddy, simply add a CNAME that points to an IP as a wildcard (if you need further explanationm shoot me an email):

* [ip]

That should take care of that... now you will need a static IP on your IIS site.

I handle this through code to do some validate (instead of a MOD_REWRITE type scenario.

On my index.cfm, do this:

<cfif listFirst(cgi.server_name, '.') EQ "something">
<!--- here you and do an include or a server-side push to hte content --->
</cfif>

You can also get fancy and do this:

<cfquery name="qCheckAccount" datasource="MyDSN">
select top 1 accountID
from accounts
where accountName = '#listFirst(cgi.server_name, '.')#' <!---(you can use queryparam, simplied to show example) --->
</cfquery>

<cfif qCheckAccount.recordCount>
<!--- include file or do server-side push --->
<cfelse>
<!--- normal HOME PAGE CONTENT --->
</cfif>

Comment 24 by Raymond Camden posted on 6/6/2009 at 6:47 PM

@Jon: So, to be clear, if you _did_ want to copy the files, that would be a simple cfdirectory call. The hard part would be getting Apache to say, "For this new site, use path X as root". Luckily Apache' config is text based, and you can restart Apache with the command line (cfexecute).

I'd still recommend using one code base though - if possible.