Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Hosted-services / web-hosting

Cheap Static Web Hosting using Azure Storage

5.00/5 (4 votes)
24 Jul 2019CPOL7 min read 8K  
Quick guide on how to save some money by switching your Azure WebApp service to a Azure Storage and a Content Delivery Network (CDN) hosting setup for static websites

Introduction

I wrote this article and published it on my website a few months ago and thought I would share. I used to 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 in late 2018, 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.

Background

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 from a storage account 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!

High Level Diagram
High Level Diagram

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.

New Storage Account
New Storage Account

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.

Storage Account Complete
Storage Account Complete

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

Secure Data Transfer
Secure Data Transfer

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.

Static Website
Static Website

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.

Deployment Pipeline
Deployment Pipeline

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.

CDN Creation
CDN Creation

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.

CDN Endpoint
CDN Endpoint

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.

CDN Rules
CDN Rules

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.

HTTPS Redirect Rule
HTTPS Redirect Rule

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.

Lowercase Rewrite Rule
Lowercase Rewrite Rule

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.

CDN Verify Record
CDN Verify Record

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.

CDN Custom Domain
CDN Custom Domain

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

CDN HTTPS Enable
CDN HTTPS Enable

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

CDN Certificate Application
CDN Certificate Application

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:

Certificate Registration Request
Certificate Registration Request

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>
CDN Domain Verified
CDN Domain Verified

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

CDN CNAME Record
CDN CNAME Record

The final step is to now redirect your www entry to the endpoint using a CNAME record if you have not already done so.

Final Note

I have run this site off a CDN for a few months 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. I would love to see how this can be further improved.

History

  • 25th July, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)