Goals
Let's try to improve the Elasticms Skeleton's performances and so also improve the referencing by google (and other search engine) of my website with Google PageSpeed Insights.
Just frontend best practices
At the end, with this post, I'll would like to make some PR in Elasticms GitHub project but the first PageSpeed Insights's recommendations are about frontend developments.
From an indexing standpoint, it's always better to display your content as soon as you can.
You should add an async
or a defer
attribute to your script tags.
Also do not forget to put your script tags at the end of the dom, just before the closing </body>
.
<script type="javascript" src="{{ asset('bundles/emsch_assets/js/app.js') }}" defer></script>
Those 2 attributes:
- `defer` will wait the loading of the page before downloading the script
- `async` will run the script as soon as the script is available
For the CSS, it's a bit more tricky. They have to be located in the header; the only attribute that works is the disabled
. You realize that, with that attribute, the CSS won't be loaded at all. Here come the trick:
<link rel="preload" href="{{ asset('bundles/emsch_assets/css/app.css') }}" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ asset('bundles/emsch_assets/css/app.css') }}"></noscript>
In this example, if JavaScript is supported by the client, the CSS will be loaded after the onload
event.
The problem with that approach is that the page will quickly display the page without CSS than switch to its final apparance. This issue is know as Cumulative Layout Shift (CLS).
We should keep this technique to load CSS rules that doesn't affect the page's rendering. I.e. the print CSS:
<link rel="stylesheet" href="{{ asset('bundles/emsch_assets/css/app.css') }}?{{ hash }}">
<link rel="preload" href="{{ asset('bundles/emsch_assets/css/app.css') }}?{{ hash }}" as="style" onload="this.onload=null;this.rel='stylesheet'" media="print">
<noscript><link rel="stylesheet" href="{{ asset('bundles/emsch_assets/css/app.css') }}?{{ hash }}"></noscript>
Remind you that you should also try to keep request counts low as possible. So your CSS finally looks like:
<link rel="stylesheet" href="{{ asset('bundles/emsch_assets/css/app.css') }}?{{ hash }}">
Yes, I know.
Remove unused CSS
When we start with Bootstrap we usally include everything, but we may want to include only what's needed:
@import "bootstrap-variables";
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/root";
@import "~bootstrap/scss/reboot";
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/images";
@import "~bootstrap/scss/code";
@import "~bootstrap/scss/grid";
//@import "~bootstrap/scss/tables";
//@import "~bootstrap/scss/forms";
@import "~bootstrap/scss/buttons";
//@import "~bootstrap/scss/transitions";
//@import "~bootstrap/scss/dropdown";
//@import "~bootstrap/scss/button-group";
//@import "~bootstrap/scss/input-group";
//@import "~bootstrap/scss/custom-forms";
@import "~bootstrap/scss/nav";
//@import "~bootstrap/scss/navbar";
@import "~bootstrap/scss/card";
//@import "~bootstrap/scss/breadcrumb";
//@import "~bootstrap/scss/pagination";
//@import "~bootstrap/scss/badge";
//@import "~bootstrap/scss/jumbotron";
//@import "~bootstrap/scss/alert";
@import "~bootstrap/scss/progress";
//@import "~bootstrap/scss/media";
//@import "~bootstrap/scss/list-group";
//@import "~bootstrap/scss/close";
//@import "~bootstrap/scss/toasts";
//@import "~bootstrap/scss/modal";
//@import "~bootstrap/scss/tooltip";
//@import "~bootstrap/scss/popover";
//@import "~bootstrap/scss/carousel";
//@import "~bootstrap/scss/spinners";
@import "~bootstrap/scss/utilities";
@import "~bootstrap/scss/print";
$fa-font-path: "~@fortawesome/fontawesome-free/webfonts" !default;
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import "~@fortawesome/fontawesome-free/scss/brands";
//@import "~@fortawesome/fontawesome-free/scss/solid";
//@import "~@fortawesome/fontawesome-free/scss/regular";
//@import "~aos/dist/aos.css";
@import "main";
Even with that config you'll drag way too much CSS rules. PageSpeed Insights continues to complain.
Now we move print rules into a dedicated CSS file:
@import "bootstrap-variables";
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/print";
@import "main_print";
Serve static assets with an efficient cache policy
This recomandation is about improve the HTTP's headers. As, with the skeleton, we can very easily generate unique asset url by adding the assets archive's hash as query parameter:
<script type="javascript" src="{{ asset('bundles/emsch_assets/js/app.js') }}?{{ hash }}" defer async></script>
Ensure that you add the hash parameter to all assets references. Now we can increase the max-age from 24h (default skeleton value) to 2 years by defining this environment variables:
APACHE_CACHE_CONTROL='max-age=63072000, public'
You can also add an immutable flag.
APACHE_CACHE_CONTROL='immutable, max-age=63072000, public'
Enable text compression
Finally, and it's probablly the only one, here is a PR to make.
We need to add the mod_deflate Apache module and acivate it for text response in the Skeleton docker image. Just a small commit.
If you aren't using the elasticms's docker image you have to activate it in you server's config.
- Goals
- Just frontend best practices
- Serve static assets with an efficient cache policy
- Enable text compression
Last posts
- Draw.io on a website
- Deploy elasticms on AWS
- Intégrer BOSA Accessibility Check dans un site web [Content in French]
- PHP - Convert Human Readable Size to Bytes
- Composer: How to use a specific branch acting like a specific revision in composer dependencies
- Stream a CSV from a JSON array
- Comment utiliser les commandes "locales" du skeleton [Content in French]
- How to extract data from a JsonMenuNestedEditorField
- Backup on AWS glacier
- Refer to environment variables in twig