Optimizing website to get 100/100 at Google PageSpeed.

Holy Grail of website performance.


It has been bothering me for last few months. Once your website is completed and you are so proud of it, you get reality check from Google’s tool. One rainy day I decided to score 100 no matter what…

Google PageSpeed result page.

My first step was code minification …

Minifying CSS and JS files is so obvious so there is no point to talk about it. Simply use any automated task runners or only tools like http://www.minifier.org

Google might suggest you compressing images to save data. What if you do not want to do this, because you would like to keep high resolution pics? Most graphics are edited in photo editors just like Adobe Photoshop. Photoshop saves some additional information about pictures:meta data and other stuff. Simply use tools like TinyPNG to get rid of unnecessary bytes. The best part is – it's happening without losing quality of the image.

You have to enable Gzip compression on your server. To do this you need to add few lines to .htaccess file which is a config file on web servers running the Apache Web Server software.)

<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font
AddOutputFilterByType DEFLATE application/x-font-opentype
AddOutputFilterByType DEFLATE application/x-font-otf
AddOutputFilterByType DEFLATE application/x-font-truetype
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
</IfModule>

Next part of making site faster is removing render bloking style aheets. To serve CSS asyncronusly I used this code in head section: 
<link rel=”stylesheet” href=”css/above-the-fold.min.css” media=”none” onload=”if(media!=’all’)media=’all’”>

and fallback for browsers with disabled javascript
<noscript><link rel=”stylesheet” href=”css/above-the-fold.min.css”></noscript>

The other CSS file has been put just before closing body tag.

Unfortunately it is not correct from web standards point of view – styles should go into the head section – however it doesn’t block rendering.

Alternatively you can use loadCSS to get the same result.

With javascript is bit different – put every single script in one file. For external scripts use CDN.

The biggest challenge was handling HTTP headers expiry date. It was not a big deal for local files because you can manipulate .htaccess once again by adding this:

<IfModule mod_expires.c>
ExpiresActive on
ExpiresDefault „access plus 1 month”
ExpiresByType text/css „access plus 1 year”
ExpiresByType application/atom+xml „access plus 1 hour”
ExpiresByType application/rdf+xml „access plus 1 hour”
ExpiresByType application/rss+xml „access plus 1 hour”
ExpiresByType application/json „access plus 0 seconds”
ExpiresByType application/ld+json „access plus 0 seconds”
ExpiresByType application/schema+json „access plus 0 seconds”
ExpiresByType application/vnd.geo+json „access plus 0 seconds”
ExpiresByType application/xml „access plus 0 seconds”
ExpiresByType text/xml „access plus 0 seconds”
ExpiresByType image/vnd.microsoft.icon „access plus 1 week”
ExpiresByType image/x-icon „access plus 1 week”
ExpiresByType text/html „access plus 0 seconds”
ExpiresByType application/javascript „access plus 1 year”
ExpiresByType application/x-javascript „access plus 1 year”
ExpiresByType text/javascript „access plus 1 year”
ExpiresByType application/manifest+json „access plus 1 year”
ExpiresByType application/x-web-app-manifest+json „access plus 0 seconds”
ExpiresByType text/cache-manifest „access plus 0 seconds”
ExpiresByType audio/ogg „access plus 1 month”
ExpiresByType image/bmp „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 image/svg+xml „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”
ExpiresByType application/vnd.ms-fontobject „access plus 1 month”
ExpiresByType font/eot „access plus 1 month”
ExpiresByType font/opentype „access plus 1 month”
ExpiresByType application/x-font-ttf „access plus 1 month”
ExpiresByType application/font-woff „access plus 1 month”
ExpiresByType application/x-font-woff „access plus 1 month”
ExpiresByType font/woff „access plus 1 month”
ExpiresByType application/font-woff2 „access plus 1 month”
ExpiresByType text/x-cross-domain-policy „access plus 1 week”
</IfModule>

It was different with external scripts because there’s no way to set the expiry date. The only javascript file I was using from external server was Google Analytics. I decided to save it on my server and this actually solve the problem. You need to point new location in Analytics script.

<script>
(function(i,s,o,g,r,a,m){i[‘GoogleAnalyticsObject’]=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,’script’,’path-to-file/analytics.js’,’ga’); ga(‘create’, ‘UA-00000000–1’, ‘auto’); ga(‘send’, ‘pageview’);
</script>

That’s how you get 100 out of 100.