NuxtJS Static Assets Compression with nuxt-compress
If you’ve ever ran a Google Lighthouse audit on your static website built with NuxtJS and saw the “Enable text compression” suggestion, then you’ve come to the right place.
In theory, you can significantly reduce your js
, css
, and html
files by compressing them into gzip
or brotli
format. Thankfully there’s a package that abstracts this task called nuxt-compress
available on Github.
However, compressing your assets during the build process is only the first step; you also have to configure your web server to serve compressed assets instead of your uncompressed assets. In addition, .html
files are not automatically compressed by nuxt-compress
(more on this later).
Let’s start!
Installation
- Install the module
npm install nuxt-compressORyarn add nuxt-compress
2. Add nuxt-compress
to your buildModules
module.exports = {
buildModules: ["nuxt-compress"]
};
If you’re using Nuxt < 2.9, you’ll need to add it to your modules
instead.
module.exports = {
modules: ["nuxt-compress"]
};
3. Run your build script (e.g. npm run generate
) and your files will be automatically compressed.
Check your built .js
files (mine is in dist/_nuxt
) and you should see files ending in .gz
and .br
My .html files are not being compressed!
You should probably notice that .html files are untouched (read about the issue here). In the meantime, as a workaround run these two commands after the files are built.
// run gzip compression
find <build_folder> -type f -iname *.html -exec gzip -k {} ';'// run brotli compression
find <build_folder> -type f -iname *.html -exec brotli {} ';'
Or as a package.json
script:
"scripts": { "postbuild": "find <build_folder> -type f -iname *.html -exec gzip -k {} ';' ; find <build_folder> -type f -iname *.html -exec brotli --best {} ';'"}.//Please note the ; in between commands for gzip and brotli
Explanation:
find <build_folder> -type f -iname *.html
searches for all .html files in your build folder (replace<build_folder>
with the path to your build folder.-exec
performs the command you specify after it{}
is the file(s) that satisfies thefind
query.gzip -k {} ';'
runs gzip compression on the file(s).-k
flag tells gzip not to delete the original file. Single quotes';'
are added to the delimiter because sometimes the terminal may not recognize that it’s a delimiter.- Similarly,
brotli {} ';'
runs brotli compression on the file(s). However, by default original files are not deleted after being compressed. - Optionally you can add
--best
flag to use the best compression quality. Seeman
pages forgzip
andbrotli
to learn more.
You’ve now learnt how to compress your static assets, but there is still one last step — configuring your web server to serve the compressed files instead of the original files.
Configuring an Apache Web server
Disclaimer: the following code snippet is taken from a blogpost written by Laurent Desgrange.
Add these lines to .htaccess
file on your web server root folder.
RewriteEngine on# Brotli
# If the web browser accept brotli encoding…
RewriteCond %{HTTP:Accept-encoding} br
# …and the web browser is fetching a probably pre-compressed file…
RewriteCond %{REQUEST_URI} .*\.(css|html|js)
# …and a matching pre-compressed file exists…
RewriteCond %{REQUEST_FILENAME}.br -s
# …then rewrite the request to deliver the brotli file
RewriteRule ^(.+) $1.br
# For each file format set the correct mime type (otherwise brotli mime type is returned) and prevent Apache for recompressing the files
RewriteRule "\.css\.br$" "-" [T=text/css,E=no-brotli,E=no-gzip]
RewriteRule "\.html\.br$" "-" [T=text/html,E=no-brotli,E=no-gzip]
RewriteRule "\.js\.br$" "-" [T=application/javascript,E=no-brotli,E=no-gzip]
# Gzip
# If the web browser accept gzip encoding…
RewriteCond %{HTTP:Accept-Encoding} gzip
# …and the web browser is fetching a probably pre-compressed file…
RewriteCond %{REQUEST_URI} .*\.(css|html|js)
# …and a matching pre-compressed file exists…
RewriteCond %{REQUEST_FILENAME}.gz -s
# …then rewrite the request to deliver the gzip file
RewriteRule ^(.+) $1.gz
# For each file format set the correct mime type (otherwise gzip mime type is returned) and prevent Apache for recompressing the files
RewriteRule "\.css\.gz$" "-" [T=text/css,E=no-brotli,E=no-gzip]
RewriteRule "\.html\.gz$" "-" [T=text/html,E=no-brotli,E=no-gzip]
RewriteRule "\.js\.gz$" "-" [T=application/javascript,E=no-brotli,E=no-gzip]<FilesMatch "\.(css|html|js)\.br$">
# Prevent mime module to set brazilian language header (because the file ends with .br)
RemoveLanguage .br
# Set the correct encoding type
Header set Content-Encoding br
# Force proxies to cache brotli & non-brotli files separately
Header append Vary Accept-Encoding
</FilesMatch>
<FilesMatch "\.(css|html|js)\.gz$">
# Serve correct encoding type
Header set Content-Encoding gzip
# Force proxies to cache gzip & non-gzip files separately
Header append Vary Accept-Encoding
</FilesMatch>
Verify that your webserver is now serving compressed assets by looking at Response Headers of one of your js
, css
or html
file, and checking if it contains the header Content-Encoding: br
or Content-Encoding: gzip
That’s all!
References
- ‘Enable text compression’ as suggested by Google Lighthouse: https://web.dev/uses-text-compression/
nuxt-compress
Github page: https://github.com/robcresswell/nuxt-compress- .html files not compressed by
compression-webpack-plugin
(the plugin used bynuxt-compress
): https://github.com/nuxt/nuxt.js/issues/6817 - ‘Pre-compression with gzip and brotli in Apache’ by Laurent Desgrange: https://blog.desgrange.net/post/2017/04/10/pre-compression-with-gzip-and-brotli-in-apache.html