So you are building a website using static .html files instead of any server side technologies such as ASP.NET. That’s cool for various reasons, but my favorite is that it allows any developer on any platform to easily contribute on GitHub. No server-side components needed. Great!

You’re almost done and decide to run performance analytics tool such as Google Page Speed on your site. Now the problems begin. Here’s some of the items that you are told to optimize:

  • Minify HTML
  • Set far-future expiration dates on static resources (JS, CSS, images etc.)
  • Use cookieless domains for static files
  • Use a CDN

You could set up build processes using Grunt to do all of this work, but it is not that simple to do – especially after you already built your website. Most of these tools require you to setup your project in a specific way from the beginning.

When you think about it, none of the above mentioned performance issues are relevant on a developer machine, they are only applicable to the live running production website. So if we could let the production server do some tricks for us to make all of this easier and without us having to modify our source code, that would be great.


While building SchemaStore.org I encountered exactly these issues and decided to create a generic and reusable solution. My idea was to let IIS handle the issues while the website could still run statically without IIS at all on a development machine.

The StaticWebHelper NuGet package does exactly that. Here’s what it does:

  1. Minifies any .html file at runtime and output caches
  2. Fingerprints references to static resources
  3. Creates a URL rewrite rule for handling the fingerprints
  4. Set’s far future expiration dates in the web.config
  5. Has support for CDNs using an appSetting

Fingerprinting is a browser cache busting technique for changing the URL to references files, so the browsers will load any changes while still featuring far-future expiration dates. Read more about fingerprinting.

#1 and #2 happens at runtime, but only once.

   <add name="FingerPrint" verb="GET" path="*.html" type="StaticWebHelper.FingerPrintHandler" />
It output caches the results so that no additional files are being created on disk and you get performance similar to static file serving. Any time a referenced JS, CSS or image file is updated on disk, it generates new fingerprints automatically. It also handles conditional GET requests (status 304).

#3, #4 and #5 are all handled in the web.config.

<add key="cdnPath" value="http://schemastore.org.m82.be/" />
<add key="minify" value="true" />

I use a custom reverse proxy CDN with nodes in both Europe and North America for serving static files cookieless. If you don’t need a CDN, it is still a good idea to use a different subdomain to handle static resources such as s.mydomain.com. StaticWebHelper supports both scenarios equally and it’s easy to setup in web.config.

For fingerprinting to work, it adds a URL rewrite rule in web.config.

<rule name="FingerPrint" stopProcessing="true">
  <match url="(.+)(\.[0-9]{18})\.([a-z]{2,4})$" />
  <action type="Rewrite" url="{R:1}.{R:3}" />

To see this in action, check out the source code of SchemaStore.org on GitHub. Especially, take a look in the web.config file.

Azure Site Extensions

If your website is hosted on Azure, then it’s really easy to let an automated Site Extension do further optimizations such as image optimization and JS/CSS minification. Read more about that here.


What's the address of your website? www.domain.com or domain.com?

There are two camps on the subject of the www subdomain. One believe it should be enforced (www.yes-www.org) and the other (no-www.org) that it should be removed. They are both right.

What's important is that there is only a single canonical address to your website – with or without www.

The web.config makes it easy for us to either enforce or remove the www subdomain using URL rewrites. There are many examples online on how to do this, but they all share 2 fundamental flaws. The rules have a direct dependency to the domain name and they don't work with both HTTP and HTTPS.

So let's see if we can create generic URL rewrite rules that can be used on any website without modifications.

Your server needs to have the URL Rewrite module installed. Chances are that it does already. Azure Websites does and so does all of my other hosting providers.

Rewrite rules need to be placed inside the <rewrite> element in web.config:

      <!-- My rules -->

So here are 2 rules that works on all domains and on both HTTP and HTTPS.

Remove WWW

This rule redirects any incoming request to www.domain.com to domain.com while preserving the HTTP(S) protocol:

<rule name="Remove WWW" patternSyntax="Wildcard" stopProcessing="true">
  <match url="*" />
    <add input="{CACHE_URL}" pattern="*://www.*" />
  <action type="Redirect" url="{C:1}://{C:2}" redirectType="Permanent" />

Enforce WWW

This rule redirects any incoming request to domain.com to www.domain.com while preserving the HTTP(S) protocol:

<rule name="Enforce WWW" stopProcessing="true">
  <match url=".*" />
    <add input="{CACHE_URL}" pattern="^(.+)://(?!www)(.*)" />
  <action type="Redirect" url="{C:1}://www.{C:2}" redirectType="Permanent" />

So there you have it. It's easy once you now how.

For more info on the URL Rewrite Module, see the Configuration Reference.


In the first part of the checklist, we looked at creating high quality websites from a client perspective and the tools that helps us do that. In this part we look at the (free) tools that will help us build high quality on the server side of the website.

Code quality

Treat compiler warnings as errors

When you compile your solution in Visual Studio it will by default allow compiler warnings. Compiler warning occurs when there is a problem with the code, but nothing that will result in severe errors. Such a warning could be if you have declared a variable that is never used. These warnings should at all times be treated as errors since they allow you to produce bad code. Keyvan has written a post about how to treat compiler warnings as errors.


The StyleCop Visual Studio add-in analyses your C# code and validates it against a lot of rules. The purpose of the tool is to force you to build maintainable, well documented code using consistent syntax and naming conventions. I’ve found that most of the rules are for maintainability and consistency. After using StyleCop on my latest project I will never build a C# project again without it.
Some of the rules might seem strange at first glance, but when you give it a closer look you’ll find that it actually makes a lot of sense.


This tool should be familiar to most .NET developers by now. It has existed for a long time and is now on version 1.36. FxCop doesn’t analyze your C# code but the compiled MSIL code, so it can be used with any .NET language. Some of the rules are the same as in StyleCop, but it also actually helps you write more robust methods that result in fewer errors.

If you use StyleCop and do proper unit testing, then you might not need FxCop, but it’s always a good idea to run it on your assemblies. Here's a guide to using FxCop in website projects. Just in case. If you own a Visual Studio Team Edition, then you already have FxCop build in.


Anti-Cross site Scripting (XSS) Library

The Anti-XSS library by Microsoft is not just a fancy way to HTML encode text strings entered by users. It uses white-listing which is much more secure than just trust any input and then HTML encode it in the response. It works with JavaScript, HTML elements and even HTML attributes.

Code Analysis Tool .NET (CAT.NET)

When your website relies on cookies, URL parameters or forms then it’s open for attacks. That’s because all three of them is very easy to forge and manipulate by hackers and robots even. By using the CAT.NET add-in for Visual Studio you can now easily analyze the places in your mark-up and code-behind that is vulnerable to those kinds of attacks. CAT.NET analyzes your code and tells you exactly what the problem is. It’s easy to use, understand and it lets you build more secure websites.


Google’s maps API now supports reversed GEO lookup which allows you to find an address based on geo coordinates.  All you need is a latitude, a longitude and this handy method:

private const string endPoint = "http://maps.google.com/maps/geo?q={0},{1}&output=xml&sensor=true&key=YOURKEY";


private static string GetAddress(double latitude, double longitude)


  string lat = latitude.ToString(CultureInfo.InvariantCulture);

  string lon = longitude.ToString(CultureInfo.InvariantCulture);

  string url = string.Format(endPoint, lat, lon);


  using (WebClient client = new WebClient())


    string xml = client.DownloadString(url);

    XmlDocument doc = new XmlDocument();



    XmlNode node = doc.ChildNodes[1].FirstChild.ChildNodes[2].ChildNodes[0];

    return node.InnerText;



It returns the address as a string.


A few days ago I needed to write some functionality to fetch an XML document from a URL and load it into an XmlDocument. As always I use the WebClient to retrieve simple documents over HTTP and it looked like this:

using (WebClient client = new WebClient())


  string xml = client.DownloadString("http://example.com/doc.xml");

  XmlDocument doc = new XmlDocument();



I ran the function and got this very informative XmlException message: Data at the root level is invalid. Line 1, position 1. I’ve seen this error before so I knew immediately what the problem was. The XML document that was retrieved from the web had three strange characters in the very beginning of the document. It looks like this:

<?xml version="1.0" encoding="utf-8"?>

Of course that result in an invalid XML document and that’s why it threw the exception. The three characters are actually a hex value (0xEFBBBF) of the preample of the encoding used by the document.

As said, I knew this error and also an easy way around still using the WebClient. Instead of retrieving the document string from the URL and load it into the XmlDocument using its LoadXml method, the easiest way is to retrieve the response stream and use the Load method of the XmlDocument instead. It could look like this:

using (WebClient client = new WebClient())

using (Stream stream = client.OpenRead("http://example.com/doc.xml"))


  XmlDocument doc = new XmlDocument();



Often there are situations where the WebClient isn’t well suited for this or one might simply prefer to use the WebRequest and WebResponse classes. Still, the solution is very simple. Here is what it could look like:

WebRequest request = HttpWebRequest.Create("http://example.com/doc.xml");

using (WebResponse response = request.GetResponse())

using (Stream stream = response.GetResponseStream())


  XmlDocument doc = new XmlDocument();



This is something that can give gray hairs if you haven’t run into it before, so I thought I’d share.  

If you have any issues with the three preample characters when serving - not consuming - XML documents, then check out Rick Strahl's very informative post about it.