Insofar as it is my first time using Grav CMS, all things that I've done here are just my experience. I am not sure about all the steps, but I just wanted to be close to mark 100 in service web.dev for Measure page quality. Let's see what I got.

Before the start

Need to say I use Grav CMS v1.7.33 - Admin v1.10.33.1 with Mediator theme  v1.5.1. This theme is free open source and minimal with minimum garbage code. Without optimization it looks like this.

without-optimization

After Optimization the picture is better, not perfect but closer to 100.

after-optimization

Perfomance of Grav CMS

This is the biggest part. I spent much time reading and applying all the details to make Grav cms working faster.

Eliminate render-blocking resources

Common problem of many websites, but solution almost universal for all CMS including Grav. For JS I add function async.

<script type="module" src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.esm.js" async></script>

For CSS possible to use HTML onload Event Attribute...

<link rel="stylesheet" href="/user/plugins/prism-highlight/css/themes/prism-base16-monokai.dark.css" media="print" onload="this.media='all'">

For fonts I use function preload and format woff2 have a better compression.

<link rel="preload" href="/fonts/Manrope-Medium.woff2" as="font" type="font/woff2" crossorigin="">

Properly size images

This problem is solved very easily, but if you know exactly what is inside. Here it will help Responsive Images.

In my example max-width this theme is 640px. So in post we don't need a larger size than 640px. In markdown use option derivatives.

![name](name.webp?derivatives=[100,360,640])

Image elements do not have explicit width and height

Grav CMS have a good option, it is called auto sizes. That option just adds attributes width and height in each image that you will upload. But it is disabled by default. To enable this feature we must edit file system.yaml in path /system/config/

images:
   auto_sizes: true

But it is not applying automatically. You should add to each image in markdown like this.

![name](name.webp?autoSizes=true "name")

Serve images in next-gen formats

Image format webp and avif can be helpful in this question. But avif format throws an error when I try to upload. Official forum Grav CMS still don't have a solution yet. Probably nobody uses that.

Fortunately with webp format no problem. First of all we need to download cwebp utility. Now I use Fedora Linux 36 and I have cwebp utility in the repository. But if you have Windows or Mac OS, you can download too. This software doesn't have a GUI but syntax is very simple. Just an example.

cwebp -q 80 example.jpg -o example.webp

Here it is: -q 80 percent of quality. So 20% compression and economy of size is not bad.

Ensure text remains visible during webfont load

Very small and easy problem and can solved by adding to file main.css or style.css one string

font-display: swap;

Accessibility of Grav CMS

One more small problem, we can solve by adding an attribute defined in the WAI-ARIA(opens in a new tab) specification. For example my logo link look like this:

<a href="/" aria-label="Go to home page" class="logo-readium"><span class="logo" style="background-image: url(/user/images/logo.png)"></span></a>

Best Practices

Ensure CSP is effective against XSS attacks

Actually I don't understand why Google takes care only about Content Security Policy and other Policies matter nothing. By default Grav CMS has a very poor statistic. You can scan your security headers to understand what you've got.

Security-headers-default

On that page you can check what headers are missing and read how to fix it. Each website is individual and there is no unified recipe. Different websites = different options. For my blog I choose the next headers. I use Apache web server so in root directory I must add headers in the file .htaccess

Header set X-Content-Type-Options nosniff
Header always append X-Frame-Options SAMEORIGIN
Header set Content-Security-Policy: "upgrade-insecure-requests";
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header set X-XSS-Protection: 1; mode=block
Header set Referrer-Policy "no-referrer-when-downgrade"
Header set Expect-CT 'enforce, max-age=43200, report-uri="https://somedomain.com/report"'
Header always set Permissions-Policy "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()"
Header set Cache-Control "max-age=31536000, public"

Result of this manipulation is below.

Security-headers

Search Engine Optimization advice

Document does not have a valid rel=canonical

In my case, I already have a string with rel=canonical.

<link rel="canonical" href="{{ page.url() }}">

But it is not working properly. For correct working this string should be like this.

<link rel="canonical" href="{{ page.url(true, true) }}" />

Wrap up

Those were only basic things which helped me to get a high level of performance. Grav CMS is a very user friendly and powerful system I highly recommend for private blogs and other similar websites. Also I want to advise you to read the article Optimizing a Grav-Installation by Ole Vik, he described more specific things and tricks. One more article is Optimizing Grav page speed by C.Y. Wong has not bad information about how to get almost 90 percent of the performance.