Brotli vs GZIP compression | Surprising compression result | Brotli Power

Suneet Bansal
Jun 29, 2019 · 7 min read

Today web sites are struggling with page load time due to network latency page takes much time to load.

Site speed is one of Web Engineer’s major engineering priorities, as faster site speed directly correlates with higher engagement. There are multiple approaches to speeding up a website. One of the most effective ways might seem obvious: the fewer bytes members need to download to the browser, the faster your website is. To achieve this, you can not only optimize the raw size of your JavaScript and CSS, but also use a superior compression algorithm to reduce the bytes on the wire.

Network would take enough time to load the page if page size is bigger. And you can really optimize the page loading time if you can compress the file as much as you can.

Why is Brotli Important?

A few years ago Brotli compression entered into the webperf spotlight with impressive gains of up to 25% over Gzip compression. The algorithm was created by Google, who initially introduced it as a way to compress web fonts via the woff2 format. Later in 2015 it was released as a compression library to optimize the delivery of web content. Despite Brotli being a completely different format from Gzip, it was quickly supported by most modern web browsers.

How much will it benefit your website performance?

Serving brotli compressed files helps reduce transfer size for HTML, CSS, JS and SVG files, but not JPG, PNG, WebP, GIFs.

Brotli compression highlights:

  • 14% smaller than gzip for JavaScript
  • 21% smaller than gzip for HTML
  • 17% smaller than gzip for CSS

Is Brotli enabled and supported on browser?

Request header accept-encoding will show br if supports Brotli and server should send response header content-encoding as br.

How to generate Brotli compress files?

Brotli compression can be CPU intensive. This means, compressing your files into brotli format through web server while serving a request is a bad idea. Instead, you should pre-compress and store brotli compressed files for your HTML, CSS, JS and SVG files. This should be done during build & deploy. You can use one of the following to brotli compress your files:

With any of the above, your goal should be to have brotli compressed files next to your regular files (style.css should have a corresponding style.css.br)

How to enable Brotli on Node server — Dynamic Compression?

Dynamic compression involves compressing assets on-the-fly as they get requested by the browser.

Pros:

  • Creating and updating saved compressed versions of assets does not need to be done.
  • Compressing on-the-fly works especially well for web pages that are dynamically generated.

Cons:

  • Compressing files at higher levels to achieve better compression ratios takes longer. This can cause a performance hit as the user waits for assets to compress before they are sent by the server.

The server.js file is responsible for setting up the Node server that hosts the application.

var express = require('express');

var app = express();

app.use(express.static('public'));

var listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
});

All this currently does is import express and use the express.static middleware to load all the static HTML, JS and CSS files in the public/directory (and those files are created by webpack with every build).

To make sure all of the assets are compressed using brotli every time they’re requested, the shrink-ray module can be used. Begin by adding it as a devDependency in package.json:

"devDependencies": {
//...
"shrink-ray": "^0.1.3"
},

And import it into the server file, server.js:

var express = require('express');
var shrinkRay = require('shrink-ray');

And add it as a middle-ware before express.static is mounted:

//...
var app = express();

app.use(express.static('public'));

// compress all requests
app.use(shrinkRay());

Now reload the app, and take a look at the bundle size & Content-Encoding response header in the Network panel: Content-Encoding should now be reflected as br and the bundles size will also reduced.

How to enable Brotli on Node server and client side? — Static Compression

The idea behind static compression is to have assets compressed and saved ahead of time.

Pros:

  • Latency due to high compression levels is not a concern anymore. Nothing needs to happen on-the-fly to compress files as they can now be fetched directly.

Cons:

  • Assets need to compressed with every build. Build times can increase significantly if high compression levels are used.

On client, you first have to apply Brotli compression on JS/CSS/HTML files and generate .br files for all JS/CSS/HTML files.

Assuming you have client code structure as:

Firstly you will add brotli npm package under package.json:

"devDependencies": {
//...
"brotli": "^1.3.2"
},

Create compress.js file under web folder and put below content:

const brotli = require('brotli');const fs = require('fs');const brotliSettings = {  extension: 'br',  skipLarger: true,  mode: 1, // 0 = generic, 1 = text, 2 = font (WOFF2)  quality: 9, // 0 - 11,  lgwin: 12 // default}fs.readdirSync('dist/').forEach(file => {if (file.endsWith('.js') || file.endsWith('.css') ||   file.endsWith('.html')) {   const result = brotli.compress(fs.readFileSync('dist/' + file),    brotliSettings)   fs.writeFileSync('dist/' + file + '.br', result)}})

Above file would generate .br files under web -> dist folder.

But to run above file, just add below npm task under package.json:

"scripts": {
//...
"brotli:compress": "node compress.js"
},

Now run “node compress.js” under web folder and check dist folder. “.br” files for all (js/css/html) bundle files will be generated.

Next, tell the server to send these brotli-compressed files whenever their original JS versions are being requested. This can be done by defining a new route in server.js before the files are served with express.static.

var express = require('express');

var app = express();

app.get('*.js', (req, res, next) => {
req.url = req.url + '.br';
res.set('Content-Encoding', 'br');
next();
});

app.use(express.static('public'));

app.get is used to tell the server how to respond to a GETrequest for a specific endpoint. A callback function is then used to define how to handle this request. The route works like this:

  • Specifying '*.js' as the first argument means that this works for every endpoint that is fired to fetch a JS file.
  • Within the callback, .br is attached to the URL of the request and the Content-Encoding response header is set to br.
  • Finally, next() ensures that the sequence continues to any callback that may be next.

Because some browsers may not support brotli compression, confirm brotli is supported before returning the brotli-compressed file by checking the Accept-Encoding request header includes br:

var express = require('express');

var app = express();

app.get('*.js', (req, res, next) => {
if (req.header('Accept-Encoding').includes('br')) {
req.url = req.url + '.br';
console.log(req.header('Accept-Encoding'));
res.set('Content-Encoding', 'br');
}
next();
});

app.use(express.static('public'));

Once the app reloads, take a look at the Network panel once more. Done!

How to enable Brotli on Node server and Webpack— Static Compression?

The idea behind static compression is to have assets compressed and saved ahead of time.

Pros:

  • Latency due to high compression levels is not a concern anymore. Nothing needs to happen on-the-fly to compress files as they can now be fetched directly.

Cons:

  • Assets need to compressed with every build. Build times can increase significantly if high compression levels are used.

Since static compression involves compressing files ahead of time, webpack settings can be modified to compress assets as part of the build step. The brotli-webpack-plugin can be used for this.

Begin by adding it as a devDependency in package.json:

"devDependencies": {
//...
"brotli-webpack-plugin": "^1.1.0"
},

Like any other webpack plugin, import it in the configurations file,webpack.config.js:

var path = require("path");

//...
var BrotliPlugin = require('brotli-webpack-plugin');
},

And include it within the plugins array:

module.exports = {
// ...
plugins: [
// ...
new BrotliPlugin({
asset: '[file].br',
test: /\.(js)$/
})
]
},

The following arguments are used in the plugin array:

  • asset: The target asset name.
  • [file] is replaced with the original asset file name
  • test: All assets that match this RegExp (i.e. javascript assets ending in .js) are processed

For example, main.js would be renamed to main.js.br.

When the app reloads and rebuilds, a compressed version of the main bundle is now created.

Next, tell the server to send these brotli-compressed files whenever their original JS versions are being requested. This can be done by defining a new route in server.js before the files are served with express.static.

var express = require('express');

var app = express();

app.get('*.js', (req, res, next) => {
req.url = req.url + '.br';
res.set('Content-Encoding', 'br');
next();
});

app.use(express.static('public'));

app.get is used to tell the server how to respond to a GETrequest for a specific endpoint. A callback function is then used to define how to handle this request. The route works like this:

  • Specifying '*.js' as the first argument means that this works for every endpoint that is fired to fetch a JS file.
  • Within the callback, .br is attached to the URL of the request and the Content-Encoding response header is set to br.
  • Finally, next() ensures that the sequence continues to any callback that may be next.

Because some browsers may not support brotli compression, confirm brotli is supported before returning the brotli-compressed file by checking the Accept-Encoding request header includes br:

var express = require('express');

var app = express();

app.get('*.js', (req, res, next) => {
if (req.header('Accept-Encoding').includes('br')) {
req.url = req.url + '.br';
console.log(req.header('Accept-Encoding'));
res.set('Content-Encoding', 'br');
}
next();
});

app.use(express.static('public'));

Once the app reloads, take a look at the Network panel once more. Done!

I hope this article would help you to improve your website page load time!!!

If you really like this article then let’s give a clap and stay tuned for the next read!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade