When you have Jekyll-built static websites and want to easily host them, you can do it on Amazon S3. We outline the exact process + all the code necessary.
Jekyll is an excellent tool for creating static sites (from plaintext, markdown, and other basic styles) that can be easily hosted on Amazon S3, GitHub pages, Heroku or any other provider that can serve HTML files.
In this article we will show you how to deploy an existing Jekyll-based website to S3 with CDN for improving the site’s performance, using CloudFront.
CloudFront for serving content over HTTPS
Serving static websites directly from S3 or GitHub pages works fine as long as you don't need to serve it over HTTPS - neither allows custom SSL certificates for HTTPS. As far as we know, putting a load balancer, reverse-proxy or CDN, such as `CloudFront`, in front is the only solution (but if you know more do not hesitate to mention them in the comments!).
So with that, let’s dive into our solution. We will start by creating a new `Content Distribution` on CloudFront. Visit the AWS CloudFront Management Console, click `Create Distribution`, and select `Web` as the `Delivery Method`. Fill the `Origin Settings` section as outlined in this screenshot:
In the field where we put `your_domain_or_bucket` type the S3 endpoint that can be found in the `Hosting Website` section (ie: `your-bucket.s3-website.eu-central-1.amazonaws.com`). This allows you to respect the S3 redirect rules when accessing the website through CloudFront.
Other sections can be left with default settings for now.
In the `Distribution Settings` section specify the SSL certificate you want to use:
If you don’t have a certificate added yet, then go to the `Certificate Manager` service on AWS and upload one there. It’s better to do it this way than import it directly in the Distribution Settings, because it makes managing the certificate easier (uploading updated versions, etc.).
Versioning static files
Before we start using CDN for serving content, we also need to make sure we have correct caching headers - so that unmodified files can be loaded from the browser's cache. Despite this, we will need to be able to deliver new versions whenever they are updated (even when the cache headers are still valid). To do this, we will fingerprint file names - so the browser won't find the new file in its cache, triggering a request to fetch it from our CDN.
For managing assets we will use the jekyll-assets gem. Add it to your `Gemfile`, run `bundle install` and update the `_config.yml` as below:
gems:
- jekyll-assets
assets:
autoprefixer:
browsers:
- "last 2 versions"
- "IE > 9"
prefix: '/assets'
assets:
- "main.css"
- "*.js"
- "*.png"
- "*.jpg"
- "*.eot"
- "*.ttf"
- "*.woff"
sources:
- _assets
Migrating stylesheets
We also need to move all the `stylesheet` files from the `_sass` directory to `assets/css` and then update `main.scss` to be our entry point for importing other files:
@import "variables";
@import "global";
In the above example we assume you have the following structure in your `_assets/css` directory:
- _global.scss
- _variables.scss
- main.scss
Remember to update your path to the assets in your templates and stylesheets:
- Replace `{% raw %}<img src="path/to/image.jpg" alt="some alt" />{% endraw />` with `{% raw %}<img src="path/to/image.jpg" alt:'some alt' />{% endraw %}`
- Replace `<script src="path/to/script.js" />` with `{% raw %}{% js 'path/to/script' %}{% endraw %}`
- Replace `<link rel="styleshee" href="path/to/stylesheet.css" />` with `{% raw %}%{ css main.css %}{% endraw %}`
- If you reference any files (fonts, images) in your stylesheets replace `url('path/to/file')` with `url(asset_path('path/to/file'))`
For more info visit the jekyll-assets page.
Migrating javascripts
We also need to move our scripts to `assets/scripts` folder and create a `main.js` file to reference our other files:
//= require jquery
//= require bootstrap
And include that main.js file in the template:
{% raw %}{% js main.js %}{% endraw %}
Now when you build your website with the `JEKYLL_ENV` set to `production` the gem will concatenate, compress, and fingerprint your asset files automatically.
Deploying to S3
In order to deploy our static website to s3 we use the s3_website gem. It allows us not only to push the files to the bucket, but also to create invalidation in our distribution for our *.html files. HTMLs can't be fingerprinted because we don't want to change the links every time we deploy a new version.
Add the `s3_website` gem to your `Bundle` file and run the `bundle install` command. You’ll also need to have Java installed because the gem is using Scala under the hood.
Now create a basic config file in the project root:
| s3_website.yml |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `s3_id: AWS_ACCESS_KEY` <br>`s3_secret: AWS_SECRET_KEY` <br>`s3_bucket: your-bucket-name` <br> <br>`index_document: index.html` <br>`error_document: error.html` <br> <br>`max_age: 3600` <br> <br>`gzip:` `true` <br> <br>`s3_endpoint: eu-central-1` <br> <br>`cache_control:` <br> `"assets/*":` `public``, max-age=3600` <br> `"*": no-cache, no-store, max-age=0` `must-revalidate` <br> <br>`cloudfront_distribution_id: YOUR_CLOUDFRONT_DISTRIBUTION_ID` <br> <br>`cloudfront_distribution_config:` <br> `default_cache_behavior:` <br> `min_ttl: <%= 60` `* 60` `* 24` `%>` <br> `aliases:` <br> `quantity: 1` <br> `items:` <br> `- www.your-website.com` <br> <br>`cloudfront_wildcard_invalidation:` `false` |
We disable caching (`no-cache, no-store, max-age=0 must-revalidate`) for all files except assets (those will be HTML files anyway unless you use a custom setup) .
Let's apply those settings to our `CloudFront` distribution to check if everything is configured correctly:
bundle exec s3_website cfg apply
If you get something similar to the message below, it means the website is ready to be deployed:
Applying the configurations in s3_website.yml on the AWS services ...
Bucket your-bucket now functions as a website
No redirects to configure for your-bucket bucket
Bucket your-bucket is now readable to the whole world
Detected an existing CloudFront distribution (id 123456789) ...
Applied custom distribution settings:
:caller_reference: '123456789'
...
To deploy the website to S3, simply use the push command:
bundle exec s3_website push
If you would like to check which files are going to be sent first, then you can pass the `--dry-run` flag to the above command.
This will create invalidation on CDN. To check its status, go to `CloudFront`, select your distribution and visit the `Invalidations` tab. Usually it takes a few minutes before the HTML file is invalidated in CDN and a new version from your S3 bucket is fetched.
For more deployment configurations, organizing your website infrastructure, or website development in general, you can ask the experts at iRonin for assistance. Our team of highly skilled specialists have deep experience and are available to take a closer look at all your web development needs.