Modern JAMstack deployment



Congratulations on your decision to use the JAMstack in your project! After your coding and harvesting the power of JavaScript, APIs, and markup is done, you probably want to host your cutting-edge static web app somewhere so that is not only available underhttp://localhost:8080. If that’s the next thing on your to-do list, because it certainly is what I do on every new project, then maybe the following post will help you get started more quickly with JAMstack deployment.

This post describes the steps needed to host your shiny new web app on three very different providers with continuous deployment and transport layer security enabled out of the box. Everything described in this post can be found in the linked Git repository.

Please note that in order to be able to replicate all of the described steps in this post, you are going to need the following:

  • a domain hosted on a provider which allows custom CNAME records
  • an active account for the particular service provider

There are definitely more service providers out there which offer the required functionality and although their offerings vary, the integration into an existing project and the end results aren’t so different from the ones depicted here.

Scenario and application

I’ve prepared a very simple html page (to be found here) which mostly displays static content but does execute two remote requests to a predefined API. I opted to use this simple approach and not go for a full-blown modern SPA framework, because the concepts involved in deploying and serving the html+js are the same and no extra building or packing is required.

The implementation details of said remote API are not relevant right now, the information they provide is a third-party call to a geo IP database and a predefined explanation about a given cloud service/provider.

The end results looks like this, when hosted locally:

simple html page

So now that we have the static page sources, there is nothing stopping us from deploying this monster of a page and making it available on the internet.


Let’s take a look at the “ready-to-use” providers as they are generally easy to set up and deploy. They also provide many extra features like an already available CDN network and valid modern TLS enabled out of the box.


Netlify is a static website hosting provider that redefines developer experience and ease of deployment. They have many advanced features like linked auth providers, worker lambda functions, automated artifact building, SSL, DNS management, basic auth support and many more. For this primer, their free plan suffices, so that’s what I’m going to be using.

After registering and opting for a free plan, one needs to link the project sources to Netlify.

Because of the fact that my project is hosted on GitHub, I just go to “Create new site” and select GitHub as the desired provider and grant Netlify access to the correct repository.

Creating a new site on GitHub

I am going to be building from the master branch, so let’s just leave the options as they are.

Building from the master branch

And that’s it. The site is instantly live and available under a unique Netlify subdomain – something like The cool thing about linking your repository to Netlify is that any commits on it instantly result in a redeploy.

If you would like to use your domain name, you have the option of using Netlify as a DNS hosting provider or create a redirect. I’m going to create a CNAME redirect. For this to work, you have to add a custom domain to your site via the Netlify admin console.

adding a custom domain to your site via the Netlify admin console

Lastly, create a CNAME DNS entry on your domain with your DNS hosting provider and point it to the unique Netlify URL:

CNAME jamon-netlify

Your deployed site should now be available underhttps://jamon-netlify.YOUR-DOMAIN.TLD (for our demo it’s and we wrote exactly 0 lines of configuration code to set everything up.

GitHub Pages

GitHub Pages has been around as long as GitHub itself and provides a hosting service for static web pages for registered users. It is not as feature-rich and flexible as some of the other services described here, but is quick to setup and baked into GitHub. If you are already hosting your project on GitHub, it is essentially a no-brainer, because all of the file changes are instantly deployed/live.

To set everything up, you need to go to the settings page of the repository you are willing to host and select the Git branch where your web site code resides. In the case of our demo, it will be master again.
Setting up the repository

As advertised, the site is instantly published and available online under which translates to for the project described here.

If, again, you rather opt out for your own domain, GitHub Pages lets you do this also. First you must provide your custom domain name.
providing your custom domain name

Then create a CNAME DNS entry on your domain with your DNS hosting provider and point it to your GitHub Pages URL:

CNAME jamon-github

As mentioned before, your deployed site should now be available underhttps://jamon-github.YOUR-DOMAIN.TLD (for our demo it’s – no further steps needed.

AWS S3 / CloudFront

If you haven’t been living under a rock for the past decade, then you should already be aware of AWS and its multiple services. Because of the fact that there are so many services, there are so many ways to host a static site on AWS. I’m going to focus on the simplest one I know – using S3 buckets.

Let’s start by creating a shiny new S3 bucket:
creating a new S3 bucket

Then set up its permissions to allow public read access:
allowing public read access

Add a public read policy:

Adding a public read policy

The policy that grants everyone read access to the bucket’s content is:

Now let’s add the content to the bucket:

adding content to the bucket

All that is left now is enabling the static website hosting feature of the bucket:
enabling the static website hosting feature

Now the content of the S3 bucket is live and can be accessed under (in this case it’s

Before celebrating the successful deployment, there are two things missing: continuous development and custom DNS support. As with everything in AWS, there are many ways to accomplish this and they are probably all correct if they work for you. For the purpose of this demo/blog post, I’ve chosen to employ AWS CodePipeline in combination with CloudFront.

Setting up CodePipeline for this project is easy, because no actual building is required. The “code” is being checked out of the GitHub repository and pushed into the previously configured S3 bucket ultimately replacing its contents.

So let’s create a continuous delivery pipeline:
creating a continuous delivery pipeline

Add GitHub as source and choose web hooks as a trigger method:
choosing web hooks

Skip the build stage as the simple demo project requires no building:
skipping the build stage

Configure the actual deployment. We want to use our previously created S3 bucket for this. Remember to select the extraction option as code pipeline zips the artifacts by default.
Configuring the actual deployment

This should do it and our code should get automatically deployed on every repository update. Let’s check if everything is working by triggering a build via a commit in the repository:
triggering a build

For providing TLS, CDN support, as well as allowing custom domain support, I’m going to be using AWS’ content delivery network – CloudFront. Prior to setting up CloudFront, we must import our SSL certificate into the AWS Certificate Manager in the *us-east-1* region (yes, even if everything else is in a different region).
importing SSL certificate intom the aws certificate manager

After this, the overview page in the AWS console should look something like this:
JAMstack on aws

Now we are going to configure the distribution so that the S3 content is only available via CloudFront, which will disable access to the previously exposed S3 bucket URL. This is needed so that we can have a HTTP-HTTPS redirection, a valid SSL certificate for the CloudFront part, the ability to use our own subdomain and to be able to fully harness the AWS content delivery network located around the globe.

Let’s start by creating a new CloudFront distribution and choose the web delivery method:
JAM on AWS CloudFront

Set up the distribution by filling out the origin domain name (the one exposed via the static website hosting option of the S3 bucket). Select said S3 bucket as origin and opt for adding the new policy to it so that the appropriate permissions are added automatically. Also, configure a HTTP redirect from HTTP to HTTPS so that our site is only available over HTTPS.
Web name redirect

Remember that policy, which we added on the S3 bucket before? Well, we don’t need it anymore, so it’s safe to delete it. This only leaves the freshly generated one from CloudFront:

For the distribution settings, fill out the desired CNAME and select the previously added certificate. Don’t forget to select a recent security policy.
AWS CloudFront Distribution

Finally, we need to create a CNAME DNS entry with your DNS hosting provider and point it to the AWS CloudFrount URL:

CNAME jamon-aws

After all of the configuration work is done, the deployed site should be available underhttps://jamon-aws.YOUR-DOMAIN.TLD (for our demo it’s

Closing thoughts

I really like working with JAMstack-based applications which offer rich and powerful user experience and live in the browser. The fact that there are so many ways to deploy them and make them generally available with ease puts a smile on my face every time. Unfortunately, I still often find myself writing complex Nginx configuration files and planning load balancing strategies.

Hopefully this post on JAMstack deployment made you at least consider giving a JAMstack-dedicated provider a chance.
If you are interested in a hosting provider not covered here, write me a comment and I’ll happily include it in a follow-up piece.

Sources, links and generally useful information

Dobroslav Totev is employed as a software developer/consultant at codecentric AG in Hamburg. He develops mostly in Java and Typescript and is always keen to learn new things and implement and apply those in a meaningful way. Currently he spends most of his time working with everything which lies between a modern web front and a reactive back end.


  • Erik

    Interesting blog post.

    How would I get a letsencrypt SSL certificate in these scenarios and update it automatically?

    Would I need to use CORS? The backend uses a different origin then the html is served from.

    Btw: the links are wrong. It’s … not

    • Dobroslav Totev

      13. February 2019 von Dobroslav Totev

      Thanks for pointing out the typo.

      I’m guessing you want to use your own subdomain with a valid certificate? It depends on how you want to manage stuff really. The easiest way is to use a service like cloudflare – they provide such a service on their free tier. That is what I’m using in this example.

      Both Netlify and AWS offer to host your domain and provide SSL for you, so if you are on one of those, than there is that option. GitHub is a bit tricky (that is the reason there are so many hosted sites there with broken certificates), I would go with something like cloudflare in this case.

      If you have a backend under a different origin – then yes CORS will be an issue. I opted to use a lambda as an API backend for this post and had to set CORS to *. You can take a look at the code at GitHub. But back to your question, if you are using Netlify, you can define a proxy and let the service do the proxying for you ( I am using such an approach in production, as this is something I was already doing with Nginx.

      Hope this helps!


Your email address will not be published.