Vernice.js is designed as a middleware written in Node.js that acts as an intermediary between Ghost and Varnish. It operates independently of both systems, receiving webhook calls from Ghost, processing the received data, and interfacing with Varnish to perform selective cache purging.
When Ghost sends a webhook call following the publication, update, or deletion of content, Vernice.js receives this request. Ghost webhooks include a JSON payload containing relevant details, such as the URL of the modified content. Vernice.js extracts the URLs involved in the purging process, including the specific content URL and the site’s homepage.
-
Receiving Webhooks: Ghost sends a webhook with a JSON payload after content changes.
-
Extracting URLs: Vernice.js analyzes the JSON payload to obtain urlObject.pathname and urlObject.host. These details are crucial because Varnish cannot directly process a JSON payload.
-
Formatting Purge Requests: Using the extracted information, Vernice.js formats the purge requests appropriately for Varnish.
-
Sending PURGE Requests: Vernice.js sends HTTP PURGE requests to Varnish for the specified path and the homepage, allowing Varnish to selectively invalidate the cache only for the affected URLs.
Example Process Once the necessary details are extracted, Vernice.js sends a PURGE HTTP request to Varnish for the specific path and the homepage. This enables Varnish to selectively invalidate the cache for the affected URLs, ensuring users always receive the most up-to-date content. Although the command sent is of type PURGE, Varnish can be configured to translate this command into a BAN, which is an effective method for selective cache cleaning.
Directly linking Ghost webhooks to Varnish is impractical because Varnish cannot interpret and handle a JSON payload sent by Ghost webhooks. Vernice.js solves this problem by acting as an intermediary, translating the webhook information into a format that Varnish can understand.
Unlike other solutions found online and in various GitHub repositories, which tend to clear the entire cache when a post is updated or a new post is published, Vernice.js is meticulous in performing a granular and selective purge of only the updated content. For instance, in a Ghost blog with 100,000 posts, if a single post is updated, Vernice.js will purge the homepage and the URL of the updated post, leaving all other posts cached.
This approach allows for an exceptionally high HIT RATIO, making the cache particularly efficient with all the consequent advantages.
Node.js and express, axios, url, body-parser. Varnish Cache 3.0 or Higher. A Linux OS support systemd.
To properly integrate Ghost with the VERNICE.JS middleware and enable selective cache purging for Varnish, you need to configure Ghost to use Hooks via Web API. Below are the detailed steps to perform this configuration starting from the provided image.
Access the Ghost admin panel and go to Settings. Select Advanced. In the Integrations section, select the Custom tab. Click on Add custom integration.
Once you have selected the option to add a custom integration, fill in the required details to create a new integration. You will need to enter a name for the integration, such as “Varnish Purge,” and a brief description, like “Purge Varnish at content modification.” After saving the integration, options for configuring webhooks will appear.
In the Varnish Purge integration screen, click on Add webhook and fill in the webhook details. Select the event that should trigger the webhook, such as “Post Published” or “Post Updated,” and enter the target URL of the server where the VERNICE.JS middleware is running. For example, if the middleware is running on the same machine as Ghost and on port 3000, the URL might be http://127.0.0.1:3000/webhook.
Repeat the process to add additional webhooks that cover all events of interest, such as the publication, update, and deletion of posts, pages, or other content.
To ensure that the Vernice.js middleware remains active and starts automatically upon system reboot, it is necessary to configure a systemd unit. This is crucial to ensure the service is always available to handle webhooks sent by Ghost. To achieve this, we will create a configuration file in /lib/systemd/system/vernice.service containing the following code:
[Unit]
Description=Vernice.js systemd service for vernice.js Node Varnish Cache Purger
After=network.target
[Service]
Type=simple
WorkingDirectory=/root
User=root
ExecStart=/usr/bin/node /root/vernice.js
Restart=always
[Install]
WantedBy=multi-user.target
Remember to adjust the paths based on the location of the vernice.js file. In the systemd unit example above, it is assumed that the script is running in the /root directory with root privileges.
Regarding Varnish configuration, due to company policy, we do not delve into the specifics of the configuration and the related VCL. We use a specific and customized version extended with Inline C. However, the configuration can be applied simply by ensuring that the recv block in Varnish is capable of correctly handling the PURGE command by performing a selective BAN of the URL. In this example and the following VCL code, we use Varnish Cache configured to listen on the loopback interface 127.0.0.1 and TCP port 80.
Please note that Varnish does not support HTTPS and SSL termination. Therefore, you need to set up a web server, such as NGINX, Caddy, or any other reverse proxy in front of Varnish to handle HTTPS and SSL termination.
Below is a brief snippet of the configuration (in VCL - Varnish Cache Language) where the requester's IP is checked (it must match either the machine's IP or the localhost IP) before proceeding with the BAN of the URL passed by the vernice.js middleware.
if (req.http.x-forwarded-for) {
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
if (req.request == "PURGE") {
if ((req.http.X-Forwarded-For == "91.107.202.139, 127.0.0.1") || (req.http.X-Forwarded-For == "127.0.0.1")) {
ban("req.url ~ ^" + req.url + "$ && req.http.host == " + req.http.host);
}
else {
error 405 "Not allowed.";
}
}
This project is licensed under the AGPL 3.0 License https://www.gnu.org/licenses/agpl-3.0.en.html Please contribute, fork and expand it. U Are Welcome !
For questions, write me at info@managedserver.it