Jamstack sites have a tremendous advantage when it comes to hosting them. As these sites consist purely of static assets, albeit augmented by api's, you can host them directly on the internets Content Delivery Network (CDN) infrastructure. A content delivery network refers to a geographically distributed group of servers which work together to provide fast delivery of Internet content.
Hosting on a CDN has many benefits:
- Instant global availability and great download speeds: As CDNs distribute files closer to the website visitor, they get both replicated and will download faster.
- Very cost effective: Even a large site will only cost a few euro's per month. CDNs are originally intended as caches so that you could save on bandwidth and webserver costs.
- No need to manage webservers or deal with hosting providers: Today CDNs can download static assets directly from cloud storage locations, eliminating the need for webservers or hosting providers altogheter.
- Fault tolerance: CDNs are distributed by nature, and can often pull the content from multiple storage locations, resulting in redundancy and high availability of your app.
- Increased security: They offer additional security optimizations, from free SSL certificates for custom domains, to built in routing rules.
- Atomic deployments: New versions of your app can be uploaded side by side with older versions in your cloud storage. By switching the CDN endpoint origin to that new version you can ensure that all files composing your app are of the same version.
Overview of my setup
I'm using Microsoft Azure CDN backed by Azure Storage blob static websites, set up as shown in this picture.
I use this setup for all my web sites and web apps, but I will use the current site as an example in the coming sections. This post turned out rather long, but each section describes in detail how I've set it up and the sections are ordered as you would execute them if you want to set up a static site on a cdn yourself. Feel free to read them in order, or cherrypick them as you see fit.
To durabely store the static files for my sites, I've provisioned a
general purpose v2 storage account in Microsoft Azure. You can only have up to 250 storage accounts in your Azure subscription, and you need them for many purposes, so I recommend that you share a storage account for multiple websites. Each account can handle up to 20000 requests per second, which is huge considering most of the requests will be served by the cdn anyway, so it's perfectly safe to share a storage account between websites.
The storage account is configured with
Read-access geo-redundant storage replication. This replication type will copy your static assets to 3 different datacenters in 2 regions, so 6 copies, and allows reading from the secondary replicas. These secondary replicas will provide the basis for the failover configuration of the cdn later.
Many of my Jamstack sites are also Progressive Web Apps. PWA's require SSL to function, therefore I always mandate that SSL is enabled at every level. To enforce SSL on a storage account, enable
Secure transfer required on the Configuration tab. Furthermore allow public access to the blobs uploaded to the storage account by checking
Allow Blob public access.
Static website endpoint
To allow CDN endpoints to download assets from the storage account you should enable its static website endpoint. By enabling this feature it will expose a specific azure storage container called
$web of the primary replica on the primary endpoint url, and the same container from the secondary replica on the secondary endpoint url.
Next to enabling the feature, you must also configure the name of the default document. The default document will be served whenever a browser navigates to a folder. This blogpost for example is refered to as
/blog/cdn-hosting/, but it's actually served by the default document inside that folder
Upload assets to subfolders
To colocate the assets of multiple sites, or even versions of sites, in the same storage account, I recommend that you create a folder per app, and inside of it a folder per version. E.g. this website is uploaded to
$web/goeleven/1.0.0 and a future version could go to
You can upload assets to azure storage in many ways. I chose to build upload capability straight into my static site generator, but you can also do it from the Azure CLI, or use a tool like Azure Explorer, or us a custom Github Action.
CDN profile and endpoint
The second part of the setup is the CDN hosting itself.
First you need to set up a CDN Profile, which is a construct that allows you to specify the provider. I'm going with
Standard Microsoft. You can only have up to 25 of these profiles. Inside a profile, you can set up up to 25 endpoints. Each endpoint represents a website. So in total you can host 625 static websites this way.
Allow both the
HTTPS protocols at this level. Eventhough the storage account only allows HTTPS traffic, the CDN must accept HTTP as well, so that it can redirect that traffic to HTTPS.
When creating a new endpoint you must also specify an origin. This will be the primary origin for your domain. Select an origin type
Storage static website and select the origin hostname of your storage account's primary static website endpoint, in my case
As origin path you must specify the sub folder in which your app resides, e.g.
Once the endpoint has been created, you can go to the
Origins tab to create a second origin and point that one to the secondary storage endpoint,
goeleven-secondary.z33.web.core.windows.net. Provide the same origin path and protocols as you did in the first.
Next create an origin group and add both origins to it, but with different priority. The probe associated to the origin group will track the health of each storage account and switch over to the lower priority one in case of problems. Setting the endpoint up this way, will provide failover in case the primary storage account should become unavailable.
You can set up the CDN endpoint to respond to requests towards your own domain as well.
To do so you must first add a
CNAME record at your DNS provider pointing to the cdn endpoint's address.
www.goeleven.com. CNAME www-goeleven-com.azureedge.net.
Once it resolves you can validate it on the
Custom domains tab and the CDN endpoint will start responding to HTTP traffic. It will respond with an error though as the storage account only allows HTTPS traffic.
CDN managed SSL
HTTPS traffic will at this point still issue warnings though, as no SSL certificate has been set up to encrypt the traffic. Luckily the CDN endpoint can provision and manage a certificate for your custom domain, for free! (As long as your DNS record is a CNAME, more on that later)
Just click on your custom domain, hit the
Custom domain HTTPS slider, save and wait (this can take a while).
To redirect HTTP traffic into HTTPS you can set up a redirect rule in the rules engine. If the request protocol is not HTTPS, then return a 302 redirect to the HTTPS protocol.
Once this rule is set, all HTTP and HTTPS to
www.goeleven.com should be routed to the cdn endpoint.
Apex domain redirect
Traffic to the apex domain, e.g.
https://goeleven.com, will not yet work however. The DNS protocol prevents the assignment of CNAME records at the zone apex only A records are allowed.
Some DNS providers however do offer a workaround to this problem, by pointing an A record to themselves and then either actively redirecting to the CNAME, or by performing CNAME flattening or CNAME chasing to resolve the IP address of the CNAME chain.
The Azure DNS supports the latter. It's called DNS A record alias in the DNS Zone service: https://docs.microsoft.com/en-us/azure/cdn/onboard-apex-domain
Next add the apex domain to the endpoints' Custom Domain list, and it will resolve to the CDN endpoint.
No SSL on apex domain
However, a new issue will come up again: The SSL certificate is not valid for the domain. This is also problematic as certificate provisioning is only valid for CNAME records, and not for A records.
There are 2 potential solutions to this problem:
- The first option is to buy certificates yourself, upload them to Azure Keyvault, associate them to the Custom Domain names and set up a process for yourself to ensure you don't forget to renew it when it expires. This is probably the best solution, but a time intensive one.
- The second option is to redirect the HTTP traffic to the
wwwdomain, and leave the HTTPS entrypoint broken. This is not as bad as it sounds. Most of my apps are on specific subdomains anyway, and for the homepage the resolution works fine for most links and most typed entries. E.g. when people simply type
goeleven.comin the browser it will go to
http://goeleven.comand then to
https://www.goeleven.com, which will resolve fine. Only when one would explicitly type
https://goeleven.comin the browser it would issue the warning. So for now I'm settling for the second solution.
Do you know of any other workarounds?
If you know another way to get an automatically managed certificate on the apex domain, I would be really interested to learn about it.