Static site hosting in Azure
What we've learned after migrating our website to Azure.
Having recently completed the migration of our website from Netlify to Azure, we decided to share some thoughts on the entire process. This article contains a summary of our efforts, some tips, and a list of useful resources for everyone looking into hosting a static website on Azure.
During the last year, we used multiple services to organize work and host our client’s web apps. It was getting more and more frustrating to operate in such an unintegrated environment instead of a coherent ecosystem.
So, we decided to do something about it.
We migrated our e-mail accounts, code repositories, documents, and the way we host our webapps to Office 365 and Azure.
One of the steps was also setting up the hosting of our company website on Azure.
Hosting a static website on Azure
We built our website with Jekyll, which means that we don't have any back-end code to worry about. The easiest way to host a bunch of HTML, CSS, and PNG files may be to use Azure App Service, but that seemed a little bit too pricey. The solution we decided on was Azure Storage and its built-in feature of hosting static websites.
I first learned about the support for static website hosting on Azure Storage from the official Azure Blog.
The whole idea boils down to setting up a storage account, copying your site's content to the right container, and telling Azure where your index and 404 error files are. The process is straightforward unless you want to map a custom domain and enable HTTPS.
Mapping a custom domain
When setting up hosting on Azure Storage, you get a dedicated sub-domain of
azurewebsites.net domain for accessing your site's content. There is also the option to use a custom domain, but it doesn't support HTTPS which was a must-have for us. There is also no way to configure routes or rewrites, so you end up with URLs pointing directly to files like
/blog.html instead of
The solution to these problems?
Use one of the Azure CDN offerings which would support custom domains, SSL certificates, and URL rewrites for prettier links.
Setting up a DNS
In the process of migrating to Azure, we had to switch the DNS servers to Azure DNS. The reason behind was that our current DNS servers didn't have features necessary to resolve domain names into Azure resources.
Let me explain this a bit.
To make a website accessible via a custom domain, you have to set up a DNS server to resolve the domain name to the correct location. To achieve this, you set either an "A" or "CNAME" record in the DNS zone. An "A" record can point only to an IP address. In our case, the location was a CDN endpoint which couldn't be referenced by an IP address. A "CNAME" record, on the other hand, can point to another domain.
Unfortunately, due to the DNS protocol constraints, we couldn't use this feature. When you set the "CNAME" record, there can be no other record set for that domain (or sub-domain). Because we use the root domain for email and other services, and both "NS" and "SOA" records are mandatory in the zone apex, this wasn't an option. You can read more about the problem and possible solutions here.
We choose to use Azure DNS which supports alias records, in this case, an "A" record alias, which can reference other Azure resources like CDN endpoints, Storage Accounts, or Traffic Manager. So instead of setting up an "A" record, we ended up setting up an "A" alias record that points to the CDN endpoint.
Setting up a CDN
The next step was to configure CDN endpoints. Azure offers a couple of CDN options, including Akamai and Verizon. For static website hosting with HTTPS, the only one which made sense was Verizon Premium. We learned this the hard way unfortunately. Our first approach was to use Akamai, as stated in the official tutorial.
It worked, but it was impossible to redirect traffic to HTTPS when visitors accessed our website with HTTP.
Why bother having HTTPS when most of your traffic goes through HTTP?
The solution was to use Verizon Premium CDN which supports advanced rules configuration engine. By writing custom rules, we were able to redirect all traffic from HTTP to HTTPS and provide visitors with pretty URLs. The only drawback of the Verizon Premium CDN is the time since creating or updating a rule to the moment it becomes active.
According to the documentation, the process for a single rule may take up to four hours. Surprising at least. The documentation mentions that the process of rules verification is conducted manually, which explains the four hour period. It significantly extends the time to test whether your rules work or not.
After configuring CDN endpoints, I could focus on enabling HTTPS for our website. At first, it seemed easy because Azure CDN supports HTTPS with auto-generated SSL certificates for custom domains. Unfortunately, as I quickly learned, that option doesn't work for apex domains.
We were left with a second option to use external SSL certificates. The process of uploading and using our certificates was a little cumbersome. First of all, to be able to use custom certificates for a CDN endpoint, you have to set up Azure KeyVault.
Then, you have to grant permission for the CDN service to access the KeyVault to retrieve the certificates. I also had to convert the certificates to the proper format required by the CDN. The activation of HTTPS on the CDN took a few hours, which similarly to rules configuration was disappointing.
Handling a contact form
Our website contains a contact form at the bottom of each page. Netlify comes with support for contact forms. To start using it, you have to add
data-netlify="true" attribute to the form, and their servers will automatically collect data upon submission which you can later access through the dashboard.
Azure doesn't have such a feature so we had to patch it somehow. Our solution was to use Azure Functions to handle submissions. You can read my tutorial where I explain how to do it.
Automatic deployments with DevOps
For our day-to-day development, we use Azure DevOps. This step wasn't necessary, but because we run a blog, I wanted to have automatic deployments after pushing new content to the Git repository, instead of doing it manually.
I had to set up a build pipeline that would run the Jekyll build command and then copy the artifacts to the storage account. Azure DevOps comes with built-in support for Jekyll so the whole process was painless.
One major issue that I encountered that wasn't related to DevOps, was that the fresh content copied to the storage account wasn't immediately visible to visitors. It makes perfect sense for CNDs which cache content at edge nodes for better performance. The solution was to create additional rules in the rules engine of the CDN, so the cache was invalidated and updated more frequently.
The whole process of setting up the hosting of a static website on Azure took me a few days. Partly because of incomplete documentation, and partly because of the time needed to update CDN configuration. If you don't feel pressure to manage everything from a single place, I would recommend using Netlify for static website hosting, at least until Microsoft improves the support for typical scenarios. It's not that bad at the moment, but it's also not a one-click experience most of us would like to have.
A tutorial on hosting static websites in Azure Storage
Verizon Premium rules engine reference in Azure documentation
Edgecast CDN official documentation
HTTP to HTTPS redirection rules
Custom rules in the Azure Premium CDN