Static Websites using Azure Storage and CDN
- Glenn Prince
- February 18, 2019
Table of Contents
As you may or may not know, I run my site on an Azure Web Service using Hugo and a Visual Studio build pipeline (Full Details Here). I have been reasonably happy with this service, however late last year Microsoft made hosting static websites on Azure Storage generally available. There are a number of benefits in hosting your static website on Azure storage, the primary factor being cost.
Storage is ridiculously cheap, especially when your site has minimal traffic like mine. There is, however, a slight drawback for me - there are some issues around serving content and case sensitivity. We can get around this using a Content Delivery Network and get the benefit of having our site served from the closest endpoint to a user, so why not implement that!

The above image shows at a high level, what we are trying to achieve. In my case, I am moving my existing site so I want to maintain maximum uptime. I also already have a build pipeline that I am modifying. Throughout this post I will try and mention steps to undertake using my previous post if you want to establish this pipeline from scratch.

Firstly, let’s create a storage account to serve our content. Create a new storage account, select your subscription and resource group, and provide a name. The location and access tier shouldn’t matter too much as the majority of the time your content should be served from an endpoint and not directly from the storage account.

The important setting here is the account type, which must be StorageV2 to access the static site feature.

I also specified a secure transfer of data from the storage account as required. Again, I don’t think this would matter too much as most of the heavy lifting is being done by the CDN.

Once the storage account has been created you will see a static website option in the settings list on the left. Open this and set it to enabled. Make sure you specify a default index document name and an error document path. This will create a default container named $web
in the blob storage section. Once you enable this feature make sure you copy the primary endpoint setting, you will need it later. Now we have a storage account ready, let’s push our content into it. If you don’t have a build pipeline already set up, follow my previous post and replace the Azure App Service deploy step with the following. If you don’t want a build pipeline setup, ignore the next step and manually copy your content into the $web
directory. For any of the options, you must ensure your content is all lowercase.

We need to deploy our files from the build pipeline by replacing the Azure App Service deploy with an Azure File Copy step. Ensure the source is the same as before $(Build.SourcesDirectory)\docs
and select the relevant settings for your environment and storage account. The one other section worth noting here is blob prefix. It is possible to run multiple sites out of the same storage account by using a blob prefix setting here and mirroring that through the later configurations. If you want to do that, set a blob prefix value now. Run your pipeline and check to ensure that your storage account is now serving web pages.

Now we are serving web pages, let us connect a CDN to the front. Select a new CDN, specify the same subscription and resource group, and make sure that the Pricing Tier is set to Premium Verizon so that you have access to the advanced rules system.

Once the CDN is set up, create an endpoint for each website you are hosting. To take advantage of the static hosting feature built into the Storage account you don’t want the origin type to be storage, you want it set to custom. Specify the origin hostname and host header to be the storage account primary endpoint. If you used a blob prefix in your build pipeline or you are using “subfolders” for different sites within the $web
folder, specify an additional folder in the origin path. I turned HTTP off, just to enforce SSL end to end.

We are now going to specify to CDN rules to perform two specific tasks, redirecting all traffic to HTTPS and changing all of the URLs to lowercase. Open up the CDN endpoint you created, select the advanced features setting and click Manage. Open the rules engine under the HTTP Large heading.

First, add a new rule that checks if the incoming Request Scheme is Http. Then add a feature that performs a URL Redirect with a code of 301. Then map any pathing using the syntax origin-path/(.*)
to the https endpoint using the syntax https://%(host)/$1
.

Secondly, we need to add an If always rule that will rewrite any URL request to lowercase. For each endpoint on your CDN use the all syntax (.*)
to remap the destination to lower case using the syntax $L1
. Saving these rules will take a few hours to push out so let us press on a little farther before we stop to let things propagate.

Our next step is to associate our CDN endpoint with our domain. To do this in production to ensure no downtime, we first need to create a verification CNAME record. This allows as to associate a domain ( www.<domainname>
) with the Azure CDN endpoint without moving the domain just yet. You will need to do something similar on your DNS host (the above screenshot is from GoDaddy). If you are setting up a new domain, just create a CNAME record for www that points directly to the Azure CDN endpoint.

The next step is to associate your CDN with your custom domain. Open your CDN endpoint, click custom domain and specify your custom hostname.

Once this is enabled, open your custom hostname and click On for the Custom domain HTTPS.

The process of applying certificates to your custom domain can take quite a while so now is a good time to walk away and leave this process for a significant amount of time (I personally would leave it run at least overnight). If you are setting up a new domain this process will happen automatically. If you are transferring a site you will receive an email from Digicert like the below:

It is important to note here that if your WHOIS contact point is not machine-readable (is hidden, or if on GoDaddy requires a human captcha process) that you must have one of the following e-mail addresses set up and useable:
admin@<domain>
administrator@<domain>
webmaster@<domain>
postmaster@<domain>

Once the entire process is complete you should have everything verified for the domain.

The final step is to now redirect your www entry to the endpoint using a CNAME record if you have not already done so.
I have now run this site off a CDN for a week now with no hiccups. My costs have dropped from about $4 a day to either a cent per day or no cost. If this site ever increases in traffic this may raise up a little but even my most aggressive calculations of 1gb stored and 1gb transferred at each endpoint would be $3.5 per month.
Let me know if you tried this or improved on this tutorial, would love to see how this can be further improved.