Compression
Compression means that the server will compress the content (using «gzip» in most cases), resulting in the files transferred being much smaller.
Enabling compression in an Express application is easy peasy lemon squeeze. It's a setting and you enable it trough the Connect middleware like so:
app.use(express.compress());
Note that it is critical to set this before any other settings in your Express application to ensure that everything is transferred in a compressed state.
Browser caching
For me, this was the trickiest part. Browser caching is in short the how server defines some http headers on the content allowing the browser to cache the content for some pre-defined amount of time. Having mosts servers set these headers is quite easy, in Express you can do this when rendering the content, like so:
res.header("Cache-Control", "public, max-age=" + cacheTime);
The tricky part is for how long you are going to allow the server to cache content. According to Google you should set the cache time for "static content" like JavaScript and CSS up to a year. However for "dynamic" content like HTML pages, browser caching is not recommended. Now, this depends on what kind of content you are serving of course and how important it it to get new content out quickly.
Static content
For my fictional website I decided on caching static files like CSS and JavaScript for one year and then using «URL fingerprinting» to tell the browser when the file had changed. «URL fingerprinting» simply means that you append a value to the end of the URL like «styles.css?cache=1.0.0» where the "1.0.0" part could be a version number or really anything that changes. In my app I simply exposed the «package version» to the main template and used that to bust any cache.
// in app.js
// Get the css app version
var version = pkg.version || (new Date().getTime());
// Expose the version number to the templates
app.use(function (req, res, next) {
res.locals.version = version;
next();
});
// in layout.jade
link(rel='stylesheet', href='/stylesheets/style.css?v=#{version}')
This - again can be done in a multitude of ways depending on server software and such. Setting cache headers for static content in Express is easy and is done once for all content.
app.use(express.static(path.join(__dirname, 'public'), { maxAge: oneYear }));
Dynamic content (HTML)
Google prefers to look at HTML as dynamic content and in most commercial cases I totally agree. For my case - the need for having the user instantly see any changes was not so important. So I decided to set the cache for all HTML pages (templates) to one day. This is set via the cache control header mentioned above.
Server caching
In contrast to browser caching, server caching simply means that templates and data is cached on the server avoiding database lookups and I/O operations every time a user requests a page. Most CMS systems and web applications do this out of the box. For my case, the application needed to read a bunch of markdown files. These files where kept in their parsed form in memory until they changed or the server restarted, this way the content was most of the time read from memory and the application would respond immediately. How you do this depends heavily on what your server-application does - but a goal here is that the server should use a maximum of 200 milliseconds before responding to any request.
Inlining styles
Apparently this is somewhat controversial and is sometimes referred to as «critical path CSS». Basically this means that instead of linking to an external CSS file in the header of your HTML document you instead inline the CSS styles needed to render parts of or the entire page. There are some tools to help you detect which parts of your stylesheet should be inlined.
What this gives you is fewer server requests. For users on mobile networks this is a big deal. In the mobile network setting, latency is often in terms of seconds. This means that requesting additional files will add a new roundtrip of latency before your pages can be rendered, since browsers generally do not start to paint a page before the styles have downloaded and have been parsed.
You can read more about Googles reasoning and how Google Page Speed looks at this specific measurement.
Inlining CSS with Jade
This is kinda a front-end thing, but it is most practical in my option to have the server do this for you. Using Jade with Express this is as simple as including your CSS file directly into the layout.jade file.
| <style type='text/css'>
include ../public/stylesheets/style.css
| </style>
Front-end
80-90% of the end-user response time is spent on the frontend.
Start there.
- Steve Sounders
Mr. Sounders coined the quote above as the performance golden rule back in 2012. I do actually think the man knows what he is talking about, but I do want to stress that it is completely possible to utterly fuck up your entire site by creating a crappy server-application. So do both the server part and the front-end part properly is my advice.
There are many things you can do in the front-end to improve the performance of your site, let's start with minification.
Minification
Minification is the process of "compiling" static files like JavaScript, HTML and CSS files in a way that reduces their file size before serving them to the browser. This can be done by the server or before the files are uploaded to the server.
For my site I use the build in functionality of Jade to minify the templates (HTML) when serving them to the browser. These minified HTML files are automatically cached by Express when you go in to production-mode so you don't have to worry about additional server response time on this.
I use CoffeeScript (which is awesome) for scripts and Uglify.js to minify the resulting JavaScript files(s). For styles I use SCSS (which also rocks) which has a build in "compressed" mode for outputting minified CSS. The tool I use to handle these files is CodeKit, which I highly recommend for front-end designers / developers!