Published on

How I made my Nuxt.js site faster with Varnish

Authors
Table of Contents

If you’re anything like me, you want your web site or app to be fast, because it’s very important that your visitor doesn’t wait to check it out, because as a user myself, it is rather frustrating to wait several seconds for a site to load, and I instantly think that there is something wrong with my internet connection (which in my case isn’t impossible, unfortunately)

In this short article I walk you through my process of integrating Varnish into my Nuxt.js app, but the same process can be followed for anything Node.js (including Next.js, SvelteKit etc).

What is Varnish?

If you look up this question, you’ll see the following:

“Varnish is an HTTP accelerator designed for content-heavy dynamic web sites as well as APIs. In contrast to other web accelerators, such as Squid, which began life as a client-side cache, or Apache and nginx, which are primarily origin servers, Varnish was designed as an HTTP accelerator.”

Now allow me to explain this in much simpler way. As we know cache memory is extremely fast, what Varnish does is cache parts of your website and instantly send a response, instead of requesting the data from your server (Apache, nginx) every time. This makes the whole request – response process a lot faster, greatly decreasing the wait time for your visitors.

I still don’t understand how Varnish is fast

Varnish

This image (that I proudly made myself) explains exactly what happens. If the content the user has requested is already cached, it’ll speed up the process by not making a new request to the backend.

Installation & Configuration

1. First we need to install Varnish typing the following command on your server:

sudo apt install varnish (Ubuntu)

sudo yum install varnish (CentOS)

2. Edit the Varnish default configuration:

Open /etc/default/varnish with your favorite editor

nano /etc/default/varnish

or

vim /etc/default/varnish

The following default configuration appears:

DAEMON_OPTS="-a :6081 \
             -T localhost:6082 \
             -b localhost:8080 \
             -u varnish -g varnish \
             -S /etc/varnish/secret \
             -s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G"

Change this to the following configuration:

DAEMON_OPTS="-a :80 \
             -T localhost:6082 \
             -f /etc/varnish/default.vcl \
             -S /etc/varnish/secret \
             -s malloc,256m"

If you can't tell already, we’re replacing our web server with Varnish, because it’s not possible for multiple web servers to listen to port 80. (Although it's possible to have both Varnish and a web server like Apache or nginx with different configuration)

3. Routing! Edit the default varnish routing configuration

Open /etc/varnish/default.vcl with your favorite editor

nano /etc/varnish/default.vcl

or

vim /etc/varnish/default.vcl

Here's my configuration for my Nuxt.js site, should be self-explanatory what's going on!

backend default {
    .host = "127.0.0.1";
    .port = "6000"; # Replace this with your site's port
    .connect_timeout = 10s;
}

backend api {
    .host = "127.0.0.1";
    .port = "5000"; # API service port
}


sub vcl_recv {
    # Happens before we check if we have this in cache already.
    # Typically you clean up the request here, removing cookies you don't need,
    # rewriting the request, etc.
    if (req.http.host ~ "(?i)^api.example.com$") {
        # We don't want the API to cache because it's where the dynamic content comes from.
        set req.backend_hint = api;
        return (pass); # Bypass caching, this is where our dynamic content resides
    } else {
        # Everything else unset cookie and cache (TTL configuration below)
        # because everything else is handled by the API service
        unset req.http.cookie;
        set req.backend_hint = default;
    }
}

sub vcl_backend_response {
    # Set 2-minute default cache if unset

    set beresp.ttl = 60s; # Default cache TTL

    if (beresp.ttl <= 0s) {
         set beresp.ttl = 120s; # Change this however you want
         set beresp.uncacheable = false;
         return (deliver);
     }

    # I have applied a custom TTL (time-to-live) to fit my needs!
     if (bereq.url == "/") {
         set beresp.ttl = 2h;
         set beresp.uncacheable = false;
         return (deliver);
     }

    # Caching Nuxt build files, they're usually static!
     if (bereq.url ~ "/_nuxt") {
         unset beresp.http.set-cookie;
         set beresp.http.cache-control = "public, max-age=172800";
         set beresp.uncacheable = false;
         set beresp.ttl = 2d;
         return (deliver);
     }

     return (deliver);
}

sub vcl_deliver {
    # Set headers by checking if cache exists
    if (obj.hits > 0) {
            set resp.http.X-Cache = "HIT";
    } else {
            set resp.http.X-Cache = "MISS";
    }
}

One thing that might not be entirely clear is that my Nuxt.js site gets it's dynamic content from my API service, so we're making sure in this configuration the API service is not involved in the caching process.

Please make sure you got a good look at this configuration and edit the port in order to point to your Node.js (Nuxt.js / Next.js etc) app’s port.

Something very important to note here is that caching doesn’t work when cookies are involved, so we’re removing them as you can already tell (if you got a good look at the config).

This means, that if your content is dynamic, you need to set the cache TTL (time-to-live) depending on how often your content changes. In my case, I am caching the landing page for 2 hours, because the content changes every 2 hours. However, most landing pages don’t necessarily have dynamic content, so you could just cache the content for days.

After you’re done editing the varnish configuration, you must restart varnish for the new settings to be in effect by typing:

sudo systemctl restart varnish

Wrapping up

After installing Varnish, my website was able to go from, in some cases, 3 seconds to under 1 second. I know that 3 seconds is a lot, but there was a lot going on.

This has been my experience with Varnish, obviously there’s a lot of custom configuration you can implement that exactly fit your needs, as I did with my case which you can already tell by the custom configuration I have provided you with.

For help I suggest you take a look at the Varnish help if you’re trying to achieve something very specific https://varnish-cache.org/docs/7.0/users-guide/

If you would like to know how to make your site EVEN faster, stay tuned!

How faster? Varnish accomplished 1 second load time. I managed to get it to 200-300 miliseconds.

If you enjoyed or found helpful this article, sign up for my newsletter to be notified for the next one!