It was eight years ago, almost to the day, when I wrote my first blog on building a secure web server using the LAMP stack with CentOS 5. I have since updated it in 2015 and again in 2017 for CentOS 7. But almost decade later and are we still hosting WordPress sites on LAMP web servers. Sure we can harden the web server but there are so many components to think about. Apache, MySQL / MariaDB, PHP, IPTABLES, SELINUX, SSH for admin access and file transfer, then the ultimate attack vector. WordPress itself.
For blogs such as mine and many of my friends in the tech community, we don’t actually need a dynamic site. WordPress alone has 279 known vulnerabilities at the time of writing this. This doesn’t include the thousands of plugins available for WordPress that provide hackers with easy pickings of thousands of vulnerable websites. In my previous hardening guide I’ve advised to only use plugins that are absolutely necessary. The surface area for attacks on a traditional LAMP web server is ridiculous. While my former blog posts on the subject can help you build a secure web server using the LAMP stack, it’s using technology that has been around for many decades.
I’m not saying that traditional web servers should not be used. Providing they are setup correctly, have the latest updates and security best practices, then they’re great. Instead, think whether a web server is actually needed.
Static sites != 1996
You may be surprised to see that there are many awesome websites built using static sites. Now, I don’t mean they have gone back to Dreamweaver and installed an FTP client, but there are two open-source static site content generators I want to call out. Hugo and Jekyll. For example, the Spotify Developer site uses Jekyll, and so do many other hundreds of popular brands.
There are many benefits to using static sites, including:
- Security – Drastically reduce the attack surface (no LAMP or WordPress)
- Speed – No content to be loaded from a database, just HTML and images.
- Cost – Considerably cheaper than Linode, AWS EC2 or Azure virtual machines
- Developers – Leverage REST API and automate and integrate with CI/CD
- Global Access – Can leverage a CDN, get free SSL certificates (with Azure CDN or CloudFlare)
Static Sites with Azure Storage
So I thought…
How cool would it be to build a website using Jekyll and push it out to Azure storage?
To make it more like the real-world, I have also used Azure CDN (Content Delivery Network) which caches a copy of the site at hundreds of POPs (Points-of-Presence) around the world for super fast access. More on that later.
During my testing I registered cloudcat.info which uses CloudFlare DNS. However, there is no reason you can’t use Azure DNS or AWS Route 53. The CNAME for www.cloudcat.info points to an Azure CDN endpoint (https://game.azureedge.net) which has custom domains enabled for cloudcat.info with SSL. Cool huh!?
For this, I’ll assume you already have a domain name and an Azure account. If not, you can create one with 12 months of free services with $200 of Azure credit.
The high-level steps are:
- Create a free Microsoft Azure account
- Create a free CloudFlare account
- Register a domain name (or use existing) and point name servers to CloudFlare name servers
- Create an Azure Resource Group
- Create an Azure Storage account
- Enable the Static Website feature (this will create a
- Push site contents from Jekyll to
$webcontainer with Azure Storage Explorer (or use a repo for CI/CD)
- Create CDN Profile and Endpoint
- Create CNAME (E.g. www.<yourdomain>.com) to point to your CDN endpoint domain (E.g. <yourname>.azureedge.net)
- Configure CDN custom domain and HTTPS
1 – Log into CloudFlare and click on Add a Site and enter your newly registered domain name:
2 – Next, select the free CloudFlare plan and confirm.
You’ll need to find out what your CloudFlare DNS servers are as this may vary for each account. Make sure to update your domain name to point at the CloudFlare name server.
Create Azure Resource Group
1 – Log into Azure and create a new Resource Group
2 – Give it a name and then click Create
Create an Azure Storage Account
1 – Go to Storage Accounts and click Add
2 – Choose your subscription (E.g. Free Trial), select the Resource Group you just created, and give it a name.
4 – Click Create.
5 – Go to Storage Accounts and click on the name of your newly created account
6 – Under Settings click on Static Website and click Enabled. You’ll need to specify the Index document name and Error document path.
7 – Click Save
This will create a
$web container for your site contents, and also a primary and secondary endpoint URL.
Note: Make a note of the URL for your Static website primary endpoint (E.g. <yourname>.z13.web.core.windows.net)
At this stage we have a primary endpoint URL, but since we have no site content it’ll just display the error:
The requested content does not exist.
In the last step we said that we’ll use
index.html as the Index document name. If you navigate to Storage Accounts > Select the account you created > Blobs, you’ll see the
To test that it is working correctly, create a file named
index.html on your computer and add some text such as “Just Testing”. We don’t really need HTML formatted code at this point, since we just want to see if the static site works.
To upload the file you can go to Storage Accounts > Select the account you created > Blobs > $web > Upload.
Alternatively you can use Azure Storage Explorer, which you can download for free. Once you install and launch it, you’ll immediately get asked to login with an Azure account. It’s incredibly easy to use and ideal for testing.
In the real world it wouldn’t be ideal to use the Azure Storage Explorer each time you want to update your site. This is especially true if you manage a popular blog or a site that requires frequent updates. In this case I’d strongly recommend you establish a Continuous Integration and Delivery Pipeline (CI/CD). This allows you to check your site changes into a repository and then build the site when it’s time to release (publish).
This may seem a little strange at first, especially if you are used to WordPress, but it’s very much worth it!
To get started I recommend reading Gabriel McColl’s blog on using Azure DevOps (formerly VSTS) with Jekyll.
If you want to go all in with Azure then also check out Azure Pipelines.
Jekyll Static Sites (it’s cheap!)
Jekyll is a static site generator. Why do you need this I hear you ask?
The problem with static sites has always been trying to manage content updates with traditional static html files. Honestly it’s nightmare and the reason WordPress became so popular in the first place. Jekyll and other static site generators (Hugo) provide templates, similar to WordPress, minus the LAMP stack requirements (PHP, MariaDB, etc.).
Better still, Jekyll uses markdown language which I personally love. In fact I’ve been using markdown for my WordPress site with the use of a plug-in, but it’s not been that smooth sailing. Most important of all is back to the CI/CD pipeline. Static site generators like Jekyll make it incredibly easy to automate and integrate with a GitHub or Azure DevOps repo.
Once it is in place you just edit your markdown files then push the changes to your repository. Did I also mention how cheap it is? While I’m focusing on Azure Storage and CDN, you could also take advantage of Jekyll with GitHub Pages (which is free).
I’m not going to walk through the steps of using Jekyll as that’s already well documented. Take a look!
Configure Azure CDN Profile
If you have come this far you’ve either got a basic
index.html file in your blob storage (
$web) or you’ve even deployed a site with Jekyll. Let’s continue and get our CDN up and running.
As I mentioned previously, one of the significant advantages of using static sites is speed, and the use of a CDN can ensure that your site performs well no matter where visitors are located around the globe. The pricing tiers allow you to choose whether to use Microsoft’s CDN regions or those of Verizon or Akamai. There is a Premium P1 option which uses Verizon that offers analytics, custom rules, token authentication, in addition to other features you may require for larger sites.
1 – Go to CDN Profiles and click Add
2 – Provide a name for your CDN profile, choose the Resource Group we created earlier and then the pricing tier.
3 – We’ll leave ‘Create a new CDN endpoint now’ unselected as we’ll do this later.
4 – Click Create.
Create CDN Endpoint
Now we have our CDN Profile, we just need to complete the step of creating our endpoint.
1 – Click on the CDN Profile and then add an Endpoint
2 – Provide an endpoint name. This will become the domain name for your CDN (E.g. <yourname>.azureedge.net)
3 – Add the origin hostname which you copied earlier (this is your static site primary endpoint domain name)
4 – Click Add
At this point you should see something like the following:
Add Custom Domain with HTTPS
Next it’s time to configure the custom domain name to point at the endpoint we just created.
1 – Go to CDN Profiles > click on the name of your profile > select your Endpoint
2 – Add Custom Domain
At this stage head back over to CloudFlare DNS and make sure you create a CNAME record that points to the CDN Endpoint hostname. As you can see in the following screenshot, the endpoint hostname is provided. In my example I’ve used www.cloudcat.info.
3 – Click Add
As a final step we need to add an SSL certificate to enable HTTPS on the static site. The good news is that Azure CDN can provide an SSL certificate for free.
4 – Go to CDN Profiles > click on the name of your profile > select your Endpoint > select your custom domain > Enable “Custom domain HTTPS”
5 – Click Save
Note: This can take quite a while since it needs to perform domain validation. Give it time, and providing your CNAME is mapped to the CDN Endpoint, the cert should be issued. Eventually.
Once you’ve uploaded your content, you should now be able to access your static site using https://www.<hostname>, which is your custom domain name. This is a cheap alternative to using web hosting or a virtual machine as it allows you to take advantage of low-cost Azure Storage Blobs. At the time of writing this (Dec 2018), it’s $0.0184 per GB for the first 50GB per month. You’ll also have to factor pricing for the Azure CDN, but it’s still way cheaper than many other hosting options, plus you have the added benefit of the speed of a CDN for your site.
By the way, the max egress (outbound) traffic for general-purpose v2 and Blob storage accounts (all regions) is 50Gbps and it can handle up to 20,000 requests per second. If you need higher than this, then log a support ticket with Microsoft and they can provide a higher limit.
It’s a refreshing way to publish websites without having to rely on a web server. I love the idea of using static sites instead of WordPress. One of my projects for 2019 is to migrate this blog to something like Jekyll or Hugo!