Serving Favicons based on Domain

If you have a web application that supports multiple domains (perhaps powered by a CMS), then supporting different favicons (for example for different brands) can seem like a problem: the default assumption with a favicon is that it resides at http://domain.com/favicon.ico, and on a multiple domain site http://domain1.com/favicon.ico and http://domain2.com/favicon.ico etc will all point to the same physical location.

Problem
How to return a different favicon based on domain?

Solution
The easiest solution is to write a handler to serve any request for http://domain.com/favicon.ico.
Writing a handler for ASP.NET has essentially two parts: the configuration in the web.config, and the handler itself.

The web.config specifies the name and assembly of the handler, what request pattern it should respond to and which HTTP verbs are included.

<system.webServer>
<modules>
// module references removed
</modules>
<handlers>
<add name="favicon" verb="*" path="favicon.ico" type="namespace.FaviconHandler, MyApplication" />
// other handlers
</handlers>
</system.webServer>

The entry above means:

“Add a handler called “favicon”, which responds to all HTTP verbs, for any request for a file called “favicon.ico”, using the Process Request function found in namespace.FaviconHandler in the MyApplication assembly”

The handler code is a class that supports the IHttpHandler interface by implementing Process Request. The code below looks at the host name of the request coming in, and based on that picks up the appropriate file, setting the content type and HTTP status code at the same time (or 404, if the file is not found):

public class FaviconHandler : IHttpHandler
{
    public void ProcessRequest(System.Web.HttpContext ctx)
    {
        string path = getFavIconPath(ctx.Request.Url.Host.ToLower());
        string contentType = null;
        if (Path.HasExtension(path))
        {
            string extension = Path.GetExtension(path).ToLower();
            switch (extension)
            {
                case ".ico":
                    contentType = "image/x-icon";
                    break;
                case ".gif":
                case ".jpg":
                case ".png":
                    contentType = "image/ico";
                    break;
                default:
                    throw new NotSupportedException("Unrecognized image type.");
            }
        }

        path = ctx.Server.MapPath(path);

        if (!File.Exists(path))
        {
            ctx.Response.StatusCode = 404;
            ctx.Response.StatusDescription = "File not found";
        }
        else
        {
            ctx.Response.StatusCode = 200;
            ctx.Response.ContentType = contentType;
            ctx.Response.WriteFile(path);
        }
    }

    public bool IsReusable { get { return true; } }

    private string getFavIconPath(string domain)
    {

        if (!string.IsNullOrEmpty(domain))
        {
            if (domain.Contains("domain1.com"))
                return "/domain1/favicon.ico";

            else if (domain.Contains("domain2.com"))
                return "/domain2/favicon.ico";

            else if (domain.Contains("domain3.com"))
                return "/domain3/favicon.ico";

        }
        return "/domain4/favicon.ico";
    }
}

So, on the server the single IIS website instance supports multiple domains and now multiple favicons.