Use these guidelines to create blazingly fast pages.
Overview
Trying to provide the best experience possible to our customers will ensure their return to our websites. Also, improving the performance can greatly reduce the impact in our infrastructure, resulting in a more maintainable system. There are several techniques from the markup point of view that we can use in our projects to improve the performance.
Clean HTML
Beautiful HTML is the foundation of a beautiful website. To get the most of our CSS and JS, we need an equally strong HTML markup code, that will ease the path for the development.
Doctype
It is important to always declare our HTML5
doctype
properly. It must always be the first line in your document, before the HTML tag.
<!DOCTYPE html>
Character encoding
First thing inside the
head
tag should be the declaration of the character set. Even before the title of the page, to avoid character issues in the title itself.
<meta charset="UTF-8">
Indentation
This has no true impact on the performance of the page, but it goes a long way into improving it's maintainability. Properly indent all your code.
Nesting
Put special care when nesting tags. Block tags should never go inside inline tags. Also, think about the structure you want for your content, for it to make sense and generate a nice outline.
Eliminate unnecessary code
Try to avoid the overuse of
div
tags or any other element that create unnecessary structure or scaffolding. Create your page with as little elements as possible, granted that it does not loose functionality.
Naming conventions
Always try to define the design or function of the element you're naming, avoiding noise and elements that could make the element confusing afterwards. Following BEM conventions when giving ids or classes to your elements, make this easier, as we get a block name, and then all modifiers are in different, derived classes.
Page weight
The heavier your page is, the slower it will load. Taking this into account, as well as the aging infrastructures in developed countries (there are still a large number of cities relying on copper networks), and the ever-growing number of mobile users, it's very important to keep your page as small as possible.
Server configuration
This is not something that we have direct access to, but we should make sure that these configurations are in place.
The first thing is to configure the server to make use of GZIP or deflate compression, so when sending the images they get compressed, resulting in better transfer times. They can be used in conjunction with HTTP2 protocol to achieve even better load times.
Another important thing to do is enabling the cache of the sites you're developing. Drupal sites make use of cache extensively, but we can replicate or complement this behavior from our server configuration. This will ensure that when somebody is visiting a page already visited, the contents will be served from a local copy, created the first time
.htaccess file
If you have access to the
.htaccess
file of your server, here you have a true and tried configuration that can help you get a good performance:
Options +Indexes
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
AddDefaultCharset utf-8
# BEGIN add type
AddType image/x-icon .ico
AddType video/ogg .ogv
AddType video/mp4 .mp4
AddType video/webm .webm
AddType application/vnd.ms-fontobject .eot
AddType font/ttf .ttf
AddType font/otf .otf
AddType font/x-woff .woff
AddType font/x-woff2 .woff2
AddType application/vnd.ms-fontobject .eot
AddType image/svg+xml .svg .svgz
AddEncoding gzip svgz
# END add type
# BEGIN compress text files
<IfModule mod_deflate.c>
# Force compression for mangled headers.
# https://developer.yahoo.com/blogs/ydn/pushing-beyond-gzipping-25601.html
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
</IfModule>
</IfModule>
# Compress all output labeled with one of the following MIME-types
# (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
# and can remove the `<IfModule mod_filter.c>` and `</IfModule>` lines
# as `AddOutputFilterByType` is still in the core directives).
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/font
AddOutputFilterByType DEFLATE application/font-truetype
AddOutputFilterByType DEFLATE application/font-ttf
AddOutputFilterByType DEFLATE application/font-otf
AddOutputFilterByType DEFLATE application/font-opentype
AddOutputFilterByType DEFLATE application/font-woff
AddOutputFilterByType DEFLATE application/font-woff2
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/woff
AddOutputFilterByType DEFLATE font/woff2
</IfModule>
</IfModule>
# END compress text files
# BEGIN Expire headers
<IfModule mod_expires.c>
ExpiresActive on
ExpiresDefault "access plus 1 month"
# CSS
ExpiresByType text/css "access plus 1 year"
# Data interchange
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType text/xml "access plus 0 seconds"
# Favicon (cannot be renamed!)
ExpiresByType image/x-icon "access plus 1 week"
# HTML components (HTCs)
ExpiresByType text/x-component "access plus 1 month"
# HTML
ExpiresByType text/html "access plus 0 seconds"
# JavaScript
ExpiresByType application/javascript "access plus 1 year"
# Manifest files
ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
ExpiresByType text/cache-manifest "access plus 0 seconds"
# Media
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
# Web feeds
ExpiresByType application/atom+xml "access plus 1 hour"
ExpiresByType application/rss+xml "access plus 1 hour"
# Web fonts
ExpiresByType application/font-woff2 "access plus 1 month"
ExpiresByType application/font-woff "access plus 1 month"
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
ExpiresByType application/x-font-ttf "access plus 1 month"
ExpiresByType font/opentype "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
</IfModule>
# END Expire headers
# BEGIN Cache-Control Headers
<IfModule mod_headers.c>
Header unset ETag
Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure
Header always set X-FRAME-OPTIONS "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Content-Type-Options "nosniff"
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
Header set Content-Security-Policy "default-src 'self' 'unsafe-inline'; script-src 'self' https://www.google.com 'unsafe-inline' 'unsafe-eval'; base-uri 'self'; img-src 'self' https://i.ytimg.com/ https://unsplash.it/ https://selectra.info/ https://via.placeholder.com/ https://picsum.photos/ data:;"
Header append Vary: Accept-Encoding
</IfModule>
FileETag None
# END Cache-Control Headers
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
BrowserMatch MSIE ie
Header set X-UA-Compatible "IE=Edge,chrome=1" env=ie
</IfModule>
</IfModule>
Unused assets
As sites evolve, it's easy to loose track of old code or assets that linger around, making our CSS and JS files heavier than they should. If you make good use of our atomic design, this should not happen normally, but it's always good to use available tools to verify that we are not bundling unused code. Our
gulp
configuration is configured to purge the CSS in
production
environments as well as do some tree-shaking in the JS code to remove dead code.
Concatenate and minify assets
To even further down the weight of your assets, remember to concatenate them into bigger files and minify them. Even though the end result of the concatenation is a bigger file, it will drastically reduce the number of requests made to the server, resulting in a faster overall time.
Images
It is very important to get right the use of images. They convey important messages, help hugely to the design, but can ruin the experience if not loaded correctly. Following these practices, you will ensure that your site performs its best.
Formats
Depending on the use of the image, some formats might be better than others:
SVG
: This vector format is the best in terms of performance and should be used whenever possible. This does not mean that any SVG file works equally: we need to optimize our files so they perform better. Tools like SVGOMG can help you with the optimization of SVG files. Just never use them straight out of Adobe Illustrator.JPEG
: JPEG files offer you the best compression algorithms for bitmap files, but they lack alpha channel/transparency. Try to use these file format when possible, with the best compression that does not degrade the image.PNG
: This format offer a very high quality compression, with option for alpha channel/transparency, but the compression is not as strong as it is on JPEG files. Use this file when you need a transparency.GIF
: Used mainly for animated images, its quality and/or compression are very poor. Sometimes, if the animation you want to include is really big for a GIF, it's better to include avideo
tag instead. Some video codecs have really good compression algorithms that can easily beat those of GIF format.
Image sizing
Always serve the images in the size needed for it's location. Delivering a bigger image and forcing it to its container it's a bad practice that takes a huge toll in performance. If you need to have a responsive image, make use of the
srcset
attribute, to deliver specifically resized images for each breakpoint. Read more about this technique
here.
<!-- Notice how we deliver a list of images for given breakpoints -->
<img srcset="awesome-img-320w.jpg 320w,
awesome-img-480w.jpg 480w,
awesome-img-800w.jpg 800w"
sizes="(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
src="awesome-img-800w.jpg" alt="Elva dressed as a fairy">
Image lazy-loading
This technique prevent the loading the images outside of the user's visible area, resulting in faster loading times as we are not blocking the render of the page waiting for images that are effectively not seen. As we scroll through our page, and get closer to an image, it gest loaded to have it available an instant before the user scrolls it into view.
Fonts
Try to load as little custom web-fonts as possible. Most of the time, using a custom font for the headings and heros is enough to transmit the company corporative image, leaving the rest of the texts to use user-available fonts. Try these techniques to improve the font loading in your projects.
Placement of scripts
Never place your JS or CSS code inside the HTML, always make a call for it, following the given advices for each type of file. Load in the
head
the CSS and any JS code that is needed for loading purposes, leaving the rest of the JS code to load in a
script
tag just before the closing
body
element.
You can make use of modern technologies to discover above-the-fold content, and only load CSS for that content, leaving the load of the remaining code for a later stage, creating the illusion of a faster loading time.
Requests
We've talked about weight and speed, but there's another factor: number of requests. Each request that we do needs to go through a series of steps that, as short as they might be depending on the conection, they exist and keep adding up. As the calls and connections have to be made, sometimes is better to concatenate files into a single request, not adding the connection times to the overall load time.
Sometimes, configuring the cache of the requests that we have to make can help too, keeping chached versions of the files on the user's machine, not having to download static assets that won't change over time.