Before using a content delivery network (CDN) for the first time, I thought I would use them only when running a big site.
Last year I moved my personal website and our Meetup’s homepage to Netlify, a content delivery network (CDN). I was astounded about the minimal steps necessary to make it work. It simplified both the setup and the day-to-day operations for me.
This article takes you through the minimal, yet real world steps to set up the homepage of the Vue.js Frankfurt Meetup with Netlify. This includes the full build automation, deployment process and notifications via Telegram when new content is published. You should know about the basics of Linux scripting, HTTP Headers and git before reading this article. Vue.js’ Frankfurt Homepage relies on yarn or npm for build automation, therefore parts of this article rely on basic knowledge of one of the tools.
Content Delivery Networks
At its core, a Content Delivery Network (CDN) provides an infrastructure to respond to HTTP requests of web site visitors. It hosts the content – usually static or cached files – on multiple server locations around the globe to deliver it with high availability, high bandwidth and low latency from the location closest to the visitor. As add-ons they also provide
- reverse proxy services to mix static and dynamic content
- caching for resources that change infrequently
- TLS encryption and certificate handling
- scaling for high traffic loads, and
- mitigation of DDoS attacks.
Over time, they evolved in a way that you don’t need to host your own servers to publish a web site any more. This article shows you how to do this.
Static Site Generators
We built the homepage of the Vue.js Frankfurt Meetup with VuePress, a static site generator based on Vue.js. It stores all content in Markdown files (one file per page). You can add dynamic content by adding Vue components when needed. The initialisation of VuePress creates all the files needed for development and production mode.
The entire Vue.js Frankfurt website with content and build automation is available as source code on GitHub. It contains all the configuration files mentioned in this document.
To make changes to the site you first check out the GitHub repository. Then you run VuePress in the development mode where changes to a local Markdown file are automatically visible in your browser for a live preview. This resembles the edit mode of a content management system (CMS). Once the changes are complete, you commit the changes and push them to GitHub.
Setup of the Content Delivery Network
Basic Setup and the first published Site
To get started with Netlify you need a Git repository and a Domain. The Git repository can be hosted for example on GitHub, GitLab or Bitbucket. For Vue.js Frankfurt we host it on GitHub. We registered our domain vuejsfrankfurt.de at Strato, a German hosting company. There we edit the Domain Name Service (DNS) entries for our domain, but we host our content on Netlify.
DNS Entries, IPv6
Two DNS entries are necessary to forward all traffic to Netlify:
- An A record for vuejsfrankfurt.de
- An CNAME record for www.vuejsfrankfurt.de
Once I’ve enable IPv6 on the Netlify homepage, www.vuejsfrankfurt.de is reachable by IPv6 as well. This is great!
Publishing content on Netlify
Now it’s time for Netlify to build the site and serve the first content. As a first step you register for a free account at Netlify and configure a link to the git repository with content for the static site generator.
Then Netlify needs to know about the build command and directory with the HTML content to be published. You can configure your site’s build settings via Netlify’s web user interface or via a configuration file. Our meetup uses the configuration file to track the changes in the version control system.
For a single command it’s simpler to put the build command in the configuration file. But as we will extend the build in the upcoming chapters for example to check broken links, we start with a separated file. The x-setting will echo all commands on the console. The e-setting will exit the bash script once a command fails and return that exit code to the caller. See Explain Shell for a short description or Peter Bengthsson’s blog post for a longer explanation with examples.
In order for this to work the file needs to have executable permissions. You can add them after you’ve added the file to the git repository using the following command:
Once the first build succeeds, your content is live. Congratulations!
Every subsequent push to the the master branch of the git repository will trigger a new build and a fresh deployment.
Let’s encrypt: SSL for our website
When using Netlify, adding SSL to your site is only a few mouse clicks away. It uses Let’s encrypt, a well-known service for free certificates. Once the DNS settings have propagated, this is added within seconds. Let’s Encrypt certificates are valid for 90 days only, so the certificate needs to be updated every few weeks. Luckily, Netlify already takes care of that.
Redirects and 404-Page handling
The _redirects file contains all the configurations about redirects and HTTP status codes. We want all links to the index.html to redirect to the start page, as they are the same page. If we would have had an old site, we could add redirects from the old relative URLs to the new relative URLs. We could even redirect to an external site for content we offer no more.
Netlify will return any file that exists. If Netlify can’t find the file, it returns the file 404.html. Using the /* match at the end of the configuration file you can redirect Netlify to any file you want for non-existent URLs. I’ve chosen to have the default configuration in the file to make it explicit.
Performance and Caching done right
Therefore, the browser doesn’t need to re-validate them anymore. We use the max-age attribute in the Cache-Control header to tell the browser about this. To pass Google’s Lighthouse test, you need to set max-age to at least 31,536,000 seconds (one year).
With these settings your site will load faster for returning visitors.
Build Notifications and Functions
Every push to the master branch triggers an update of the website. It takes a few seconds for the site to build, and then it is online. Waiting these few seconds is sometimes annoying, and as a developer you want to be sure the build succeeded.
Netlify allows you to configure hooks and functions that trigger on build events. I once set up IFTTT to capture the web hooks, but found it tedious to test and I couldn’t pass on all the information provided by Netlify. Then I found out about Netlify Functions.
The following scripts convert the Netlify event to a Telegram message and publishes it to a group chat. In this example there are notifications for the start of the build and successful and failed builds.
The steps for the setup:
- Adding a new sub-project to the git repository. I called it lambda, but you can choose any name here.
- Extending the build.sh with a new step to generate the compiled functions.
- Extending the existing netlify.toml with the functions attribute where Netlify can find the compiled functions.
- Placing a new (!) netlify.toml in the lambda directory to specify the distribution folder relative to the sub-folder. This build process will only see this file when compiling the lambdas.
- Setting up a Telegram bot.
- Adding the configuration elements API_TOKEN and CHAT_ID as environment variables in the Netlify web UI (these confidential information must not be stored in the git repository).
Step 1: The netlify-lambda GitHub repository describes how to set up the contents of the lambda folder. Keeping all of it inside its own folder prevents WebPack and library version conflicts between your project and netlify-lambda.
For sample contents of the lambda folder see the corresponding folder in the Vue.js Frankfurt GitHub repository.
Step 5: Please have a look at the Telegram’s bots and their developer API documentation on how to setup a new bot. You won’t be able to interact with this bot as it will only send status information. Once you’ve registered the bot, you’ll receive an API_TOKEN. To get the CHAT_ID, add the bot as a manager to a group and allow it to read and post messages. Then post one message to the group using the Telegram client. Use the REST-API to get the CHAT_ID. Once you have the CHAT_ID, you can reduce the rights of the bot. The bot uses the CHAT_ID to post all notifications to this group.
Step 6: Setup the environment variables in the Netlify Web UI in Settings > Build & Deploy > Continuous Deployment > Build environment variables.
Once you’ve completed this setup, you’ll receive two notifications for each build: One that the build has started, and another that it has completed.
Preview-Feature for Pull Requests
We’ve also tried out the deploy preview feature: Netlify comments every pull request with a link to a preview page specific to the pull request.
With this feature you can review the changes of a pull request on a live site before you merge it.
Please be aware: On a public GitHub repository everyone can create a pull request. A malicious pull request might expose information you’ve passed to the build as environment variables (for example the API Key for the Telegram bot), as Netlify passes them to the build of the pull request. Therefore we’ve disabled this feature until Netlify offers a method to better protect API keys. See chapter “Limitations” below for more details.
Broken Link checker
A simple typo can break a link. Deleting a page might miss references pointing to it. Both will leave your visitor on a 404-Not-Found page.
Broken Link Checker (blc) is there to help: The following setup will check all internal links of the website. If one of the internal links is broken, the deployment will fail. We don’t check external links as we don’t want the build to fail when an external site is temporarily unavailable.
The script above will serve the static content on port 5000. Then blc will check it. If it finds an error, it will exit with a non-zero error code. The e-setting will exit the bash with non-zero error code. This will make the build fail and no content will be deployed.
To check external links, run a local build, serve it and check the links.
Limitations running on Netlify
After running for about 8 months on Netlify, here’s a summary of the limitations I found:
Storing secrets in environment variables.
The best solution I found to store secrets like API_KEY and CHAT_ID for Telegram was using environment variables. This way the secrets are not stored in the git repository, but they are still accessible in plain text from the Netlify admin UI. Netlify will use the same environment variables for all branches and pull requests. A malicious script submitted by a pull request will expose your environment variables with this setup. Services like Travis CI take additional security measures as they make secrets (they call them encrypted variables) unavailable to pull requests submitted from outside of the referenced repository. After a security incident Travis CI also filters out all passwords from logs. I hope Netlify will provide the option to hide secrets from pull request builds in the future.
Paying Money for additional users.
Netlify is only free as long there is a single user accessing the Netlify web UI. Having more users will cost you money. Sending notifications to Telegram to give the team a hint what’s wrong when a site doesn’t build successfully might defer this for some teams. Also the comments on the pull requests help to see a preview and a build status of a pull request.
Paying money for bandwidth.
There is a soft limit of 100 GB per month for the free plan. I’m well below that, let’s see if this stays this way. They describe it as a soft limit;
I assume they’ll disable the site if I exceed it too muchthey will get in touch to ask you to pay, before disabling it automatically (updated 05.02.2019).
Paying money when the number of functions called increases.
Within the free plan, there are 125,000 executions and 100 hours execution time. As long as I use the functions only for notifications, I’ll stay well below. Once I use it to provide an API, the usage will increase. Reading “Scalable: Your plan will upgrade automatically to fit your usage.” is scary: Will I need to pay for the excess usage, or will they disable the service once I reach the limit?
Purging old versions
All the old versions of the site exist, at least when I access them from the Netlify web console. The HTTP header X-Robots-Tag: noindex prevents search engines to index them. I read that you can contact the support to delete old content, for example, when you are obliged by legal reasons like General Data Protection Regulation (GDPR). I’d love to see a purge functionality in the API or on the web console.
Old files gone at redeployment
A friend of mine is hosting his content on ZEIT – they apparently offer similar flexibility. They emphasise like Netlify using a CDN for static content and lambdas for dynamic content. I think it’s worth a look if you want to start a new project, but I’m happy with Netlify for now.
In this article I’ve described how to setup a site on Netlify, configure the build process, how to check for broken links in the build process and how to send out notifications on Telegram about a started, completed or broken build.
Looking back, using Netlify was easier than setting up my own server. It was also easier than setting up a build process with Travis CI, something I’ve done previously for hosting pages on GitHub pages. The web UI allowed me to learn about Netlify’s features one by one. There is also an API and a command-line tool, but I’ve used neither.
The flexibility on how to create your HTML is amazing. It’s nice to have a web server running with SSL, HTTP/2 and IPv6, and that is managed and upgraded without manual intervention.
You’ve seen real-life configurations that will give you an idea for your own projects. If you have questions, contact me via a direct message on twitter or via email, I’m happy to help.If you found this article helpful and you want to share it, please do so via the Twitter button below. If you found this article helpful and you want to share it, do so on Twitter.