6 More Must-Do Grav Tweaks: Ready for Hacker News Traffic!

Bruno Skvorc
Bruno Skvorc

We previously looked at some essential Grav plugins with which to round off the installation of a personal blog. In this post, we’ll take a look at some additional, advanced plugins, rounding it all off with an installation of Varnish for supercaching, taking performance to a whole new level.

Grav Logo

Related Pages

Once a blog has enough posts, user retention becomes more difficult – as users find it hard to locate related or interesting posts to read, they leave your site. The related pages plugin helps with that.

Out of the box, it includes some sensible defaults for calculating relation scores between posts, and can include title scanning, content parsing, matching by taxonomies, and much more. Once the scanning has been configured, rendering the pages is a matter of including the generated list of related items in an existing template, like so:

{% if config.plugins.relatedpages.enabled and related_pages|length > 0 %}
    <h4>Related Posts</h4>
    {% include 'partials/relatedpages.html.twig' %}
{% endif %}

Note that the styling, while decent in its default form, is ultimately up to you. That’s where the next plugin comes in handy.

Custom JS and CSS without extending the theme

Sometimes, all you need to do is include a small JS or CSS modification in your pages. A full theme extension would, in that case, be overkill. That’s where the assets plugin comes in.

Once installed, you have the ability to add JS / CSS frontmatter to your pages:

{assets:js order:10}

h1 {color: red !important;}

Notice the inline_ prefix when dealing with inline CSS / JS

This then lets you easily tweak the look and feel of certain pages when needed – like adding some fancy demo logic into the mix, custom styling in case the page is of a different type than what you usually post, and more.

Having many posts is pointless if there’s no way for people to search them and find the one they’re looking for. Given that Grav is a flat-file system with no database, searching takes more than just firing off a MySQL LIKE %% query.

The simplesearch plugin (named that way purely because it uses a very simple method of searching – string matching) adds this feature. It comes with its own search field partial, and a search results page, but these can of course be fully customized.

For example, after copying the user/plugins/simplesearch/templates/simplesearch_results.html.twig file into user/themes/cacti-swader/templates/ and modifying it, here’s how the implementation looks on my own blog:

Search implementation on bitfalls.com



To make sure it works across all pages, the filters value in the configuration file must be EMPTY, not non-existent. If you remove it completely, it will fall back to the default of the plugin’s original configuration file, which limits the results to those that have a category of blog. Here’s my configuration in user/config/plugins/simplesearch.yaml:

enabled: true
built_in_css: true
display_button: false
min_query_length: 3
route: /search
filters: ""
template: simplesearch_results
  by: date
  dir: desc


While effective, this plugin will lose performance as your collection of posts grows, because it iterates through every page you have and compares its content to the given search query. The length of the posts and their number directly correlates to the search performance. It is recommended to implement a more powerful search engine once the number of posts reaches critical mass (e.g. over 1000).

Multi-language and SimpleSearch

If you’re using the language switcher by utilizing a multi-language theme, you might want to modify the langswitcher.html.twig partial to ignore the search query string when switching languages because of this. Use the instructions in the linked issue to fix the problem. And while we’re on the topic of language, to modify the string “Search results” and other values on the search results page, you can either modify the user/plugins/simplesearch/language.yaml file, or change the strings used in the search results template to use those defined in the theme’s language file.


To add a comments section, we can use the JsComments plugin. It’s an abstraction of several popular comment systems, allowing for their site-wide installation in an instant, by just modifying the config file and adding the appropriate license key or identification code.

Unfortunately, Disqus is still the most viable option today, so let’s go with that.

The Admin Panel UI intuitively offers all the options we need to set, most notably the “Enabled” and “Active” option in the main tab, and the Disqus shortname in the Disqus tab:

The disqus tab

Once done, the JS snippet needs to be injected into the template or page in which we want the comments rendered. This snippet is identical for all providers, and automatically switches out the comment system code when and if you decide on a different provider:

    {% if config.plugins.jscomments.enabled %}
        {{ jscomments() }}
    {% endif %}

Here’s how the implementation looks on my blog:

Disqus on Bitfalls.com

Difference from Official Comments

There is also an official comments plugin which provides native commenting functionality and saves the comments in local files, but it’s missing some crucial features (see list in README file) and it’s flat-file based, not JS-based, which means they don’t play nice with full page caching.

When pages with JS comments are cached, they’re loaded asynchronously after the page is shown, regardless of the level of caching, which makes sure they’re always up to date even if you cache your content very aggressively.

Image Optimization and CDNs

Any good blog post is rich with text-breaking images to increase readability. But images tend to get pretty heavy sometimes, and bandwidth is precious – especially during traffic spikes. There are two options: CDN, and image optimization.


Grav’s CDN plugin works great with pullzone CDNs like MaxCDN.

A pullzone CDN is a special type of setup many CDNs offer where, when a file is requested from your site, it is first copied to the CDN, then to all its remote locations for serving around the world, and then served back to the user.

Pullzones usually aren’t free, and need additional setting up, plus some CNAME records at your domain’s registrar, but all the effort is worth it when the speed gains and bandwidth savings are considered.

Once fully configured on the CDN’s side, implementing a pullzone in Grav is extremely simple. The plugin provides the typical yaml configuration file (fully editable in the Admin UI):

enabled: true
inline_css_replace: true
pullzone: yourdomain.cdn.com
tags: 'a|link|img|script'
extensions: 'jpe?g|png|gif|ttf|otf|svg|woff|xml|js|css'

The above means: Replace all links to resources with the yourdomain.cdn.com domain, including url() calls in CSS, and do the same for all the listed extensions in all a, link, img, and script tags. This effectively makes the CDN auto-serve all static assets.

For assets you don’t want CDNed, there’s a nocdn mode, too.

Image Optimization

Whereas a CDN will serve assets with great efficiency and over great distances, you might also be interested in merely optimizing your images and reducing their filesize. The rather specific optimus plugin ties into the Optimus image optimization service.

Optimus is an arguably much more affordable service, clocking in at $19 per year for personal projects, but it lacks the geo-diversity of a CDN and only handles images. That said, this too can lead to some astonishing bandwidth savings.

Once installed, Optimus is automatically activated and sends all images for processing to the service. There are two things to keep in mind:

  • the initial load of images will be slow, because it’ll take time for Optimus to process them and send them back
  • the images will get saved and cached locally – they get returned back to your server, so it’s still you serving them.

The ideal approach would be using Optimus to optimize images, and then forwarding those to a CDN pullzone, thus reducing bandwidth for static assets to near 0%, but at a cost of about an extra $100 per year when looked at in total.

There is another way to super-cache your site, though, and that one’s free.

Caching with Varnish

As a final, super-optimization step, we’ll take advantage of Varnish, the reverse proxy server which caches full pages for arbitrary amounts of time and serves them very, very quickly. It protects your server from traffic spikes, and saves server resources for other processes and sites.

After installing it with something like sudo apt-get install varnish, we can modify some settings files. The default value for DAEMON_OPTS in /etc/default/varnish is actually fine as it is by default:

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

Note: find out about these values in our original Varnish post

The other file to pay attention to is /etc/varnish/default.vcl, and its backend value.

backend default {
    .host = "";
    .port = "8080";

The port should be changed to the port of the regular server, and the host should be renamed to the hostname of the server as defined in the server configuration. For my example during local development, it ends up looking like this:

backend default {
    .host = "test.app";
    .port = "80";

For this to work, the /etc/hosts file of the server you’re running your development sites on needs to have the test.app entry, and you need to have the server configuration of this particular site named test.app, like so:

server {

    listen 80;
    listen 443 ssl;

    server_name test.app;


Now, the combination of these files makes sure that when visiting the page http://test.app:6081, Varnish actually fetches the content from http://test.app:80, saves it, and forwards it to the browser.

The same applies to live servers, but obviously those will differ in domain names.

Deploying for production entails the following:

  • change port for Nginx / Apache to something esoteric (9123)
  • change backend port in /etc/varnish/default.vcl to that port
  • change Varnish front end port to 80

Once done, you successfully got a Varnish-powered supercached site going, and you’re ready for those Hacker News / Reddit traffic spikes!


As you can see, there’s a lot that can be done with Grav (and around it), and we haven’t even looked at fully custom plugins or themes yet – something we’ll do in the near future.

With the setup presented in this post, and the plugins from the previous list, your blog is 100% production ready.

Have you given Grav a try yet? How do you feel about our suggestions in this post? Would you approach anything differently? Let us know!