OK so this is part
note to self and a post explaining how I setup this site with the following:
- Hosting of static content with GitHub Pages
- Custom domain mapping
- Cloudflare caching and enforced SSL
The result is a lightning quick and secure site that requires zero system administration or maintenance which was one of the critical things I was looking for. Static content is generated from a ghost website that I've got replicated across my OneDrive and then a simple
git push publishes it to the web when I'm ready.
- ghost blogging platform (runs on Node.js)
- buster python library for generating static content from ghost
- Account at GitHub
- git for version control and interfacing with GitHub
- An account at Cloudflare where I use a free account for DNS and page rules to enforce SSL + drive a permanent cache
I'll assume that you already have a personal domain and can go and create a GitHub and Cloudflare account. Setting up CloudFlare to run the DNS for this domain is outside of the scope of this article but it's pretty comprehensively documented in their Getting Started guide. I will, however, show some screenshots of how I've got things configured where relevant to the article.
As my OS of choice is Linux (where possible) / MacOS (on my laptop) these instructions will be heavily skewed in the terminal towards those platforms.
As a matter of good practice I try to, wherever possible, use virtual environments (venvs) in order to separate the code requirements from other system packages and projects.
Ghost blogging platform
Buster static site generator
Final folder structure
For reference and orientation I have the following as my final folder structure:
~/OneDrive/ |- Blog (synced with OneDrive) |- ghost <-- blogging platform + drafts \- myname.github.io <-- github repo for live site |- build-site.sh <-- create script |- CNAME <-- used for mapping custom domain |- index.html \- ... ~/Virtualenvs |- buster <-- python virtualenv \- ...
Head across to python.org if you don't already have Python installed locally where there are operating system specific instructions for Windows, Linux/UNIX, Mac OS X and Other. I'm using Python2.7 so YMMV if you want to use Python3.
Once installed, we are going to use the
pip package manager to install the
virtualenv package. As of version 2.7.9 of Python
pip is installed by default so you don't need to do anything here.
virtualenv -p /usr/bin/python2.7 ~/Virtualenvs/buster source ~/Virtualenvs/buster/bin/activate pip install --upgrade pip buster
If on MacOS you'll need to install
brew install wget
Buster is now installed and setup.
Although not supported officially, I'm going to install NVM using homebrew to manage my node instances. This is just easier all around from a maintenance perspective and, based on my testing, works just fine.
brew install nvm
As, at time of checking, Ghost only supported certain versions of Node (source) I'm going to install v6.9 which is the Long Term Support (LTS) version.
nvm install --lts
With a Node version now setup we're going to download ghost and unzip it into our
build directory. We can then
npm install and
npm start the application.
build directory is essentially going to be our local development application, replicating the main website and also storing any drafts we produce.
# define and create the build location build=~/OneDrive/Blog/ghost mkdir -p $build # download ghost latest (0.11.3) and unzip wget -O /tmp/ghost.zip https://github.com/TryGhost/Ghost/releases/download/0.11.3/Ghost-0.11.3.zip unzip /tmp/ghost.zip -d $build # install and run application cd $build npm install npm start
Setup a github repo
Once created, we're going to clone this down to our local machine so that we can start adding the static output that gets generated by buster.
cd ~/Blog git clone https://github.com/<username>/<username>.github.io
Add and commit content
Right now we have an empty repository that, when populated, can be found online at http://username.github.io.
Before we do anything else, let's add the content from our ghost site as-is and check this workflow is OK.
# define live location live=~/Blog/<username>.github.io # now change into this directory and initialise cd $live buster setup
This final command will then configure buster by asking for your repo address, which will be something like https://github.com/username/username.github.io.git. You can get this from your repository webpage just in the address bar of your browser.
Once configured, run
buster generate in order to output a folder
static in the current directory containing all of the web content for your site.
Your repository will now look as follows:
yourname.github.io |- static <-- destroyed and re-created constantly |- index.html |- ...
/static folder is destroyed and re-created everytime you run the
buster generate command. Let's commit this and push to GitHub.
git add . git commit -m 'first commit of buster output' git push origin master
Your online repository now contains the static web content generated by buster.
OK, so when you navigate to http://username.github.io you discover that nothing is displayed?
The reason for this is that everything is output by buster into the
/static folder. As such, hitting the root
/ doesn't generate anything. You can append
/static to the address and everything will display but this is pretty pants...
For the moment, let's manually create an
index.html that forwards the user automatically to the
static content (we can't use
url_rewrite or equivalent). I've already created one so just pull this down into the root.
cd $live wget -O index.html "https://goo.gl/zG7TF6" git add . git commit -m 'add custom forwarding index.html' git push origin master
This will ensure anytime you hit the repository you automatically get forwarded to
/static. We will put a better solution in place later but this will keep things clean for now.
Add your custom domain
Assuming you've now added Cloudflare as your DNS for the your custom domain we will now go in and point the
CNAME records at the GitHub servers. See guide for more details if you get stuck on this.
As per GitHub Pages the IPs on which your content can be reached are:
With the CNAME Flattening that's now available we don't really need these anymore but it's worth noting (previously you'd need to map an
A record to the IP). Head across to Cloudflare and your DNS settings at https://www.cloudflare.com/a/dns/mydomain.com
We need to create/modify two
CNAME records here, one for
www and one for the root (i.e.
mydomain.com without the
www). We will use the
root as opposed to
www as the primary method for accessing the site, but you could always change this around.
As can be seen we've mapped the
root through to the github pages url. Then, anytime someone requests
www.mydomain.com they will be mapped through to wherever the root points.
This is quite important as you only get three
page rules for free on Cloudflare and we will need all of them for a single
CNAME. Hence having both
www mapped to the same place allows this.
We need to add a custom
CNAME file to the github repo in order for our DNS settings to be honoured. Given we just set the
root to point here let's add that record.
cd $live echo mydomain.com > CNAME git add . git commit -m 'add CNAME' git push origin master
Navigating through to http://mydomain.com should now take you through to the site we just pushed.
Enable SSL and Caching
Using the aforementioned
page rules offered by CloudFlare we can now do the following:
- Force SSL for mydomain.com
- Redirect any
- Cache everything behind mydomain.com so it loads faster after the first visit
Some of this replicates what you'd expect if you were running your own webserver like nginx and was a nice bonus to find. As an example, comparing load times of an example post before and after gives a +25% improvement.
Navigate to the
page rules section for your domain in the CloudFlare control panel and create the following:
Forwarding rule for https://
www.mydomain.com to https://mydomain.com. This should be a
Always use https rule for http://mydomain.com
Cache Level rule
Cache Everything for https://jamesveitch.com
NB: Make sure they are ordered as above once you're finished.
This forces all traffic to the
root of the domain, converts all
https and then caches it.
You then need to go to the
crypto tab and ensure you have
SSL set to
Full for the site (not the
Progress so far
There are still though a couple of gotchas with this setup:
- content resides in an ugly
- as we're running ghost in development mode (which is default when you run
npm start) the robots.txt, requests, urls and template variables in the themes refer to
Enter the build script
To fix the sub-folder and robots.txt issues I created a small bash script which will perform the following:
- generate static content with buster
- overwrite site root folder with contents of
/staticwhilst keeping key files intact
- edit the robots.txt file to point to our actual site
- commit and push GitHub with a timestamp comment
To setup this in our current project.
cd $live wget -O build-site.sh "https://goo.gl/Ie0VBA" chmod +x build-site.sh
You can now run
build-site.sh from the
live repository root to generate and deploy.
After following the above you should have a functional ghost website, hosted for free on GitHub pages, with forced SSL on all pages and CloudFlare cacheing content to deliver via their CDN.