Website Performance Optimization Checklist

This is a work in progress and I need your HELP to expand this exhaustive list of tricks and tools for high performance websites! If you have any cool ideas, wicked tricks or trade secrets of Webpage Performance Optimization, please feel free to drop a word on my blog post. Let's make the web faster!
Last Updated: 07/21/2014 11:15AM America/Boise(MDT)
This is a handy checklist and one-stop shop for all the tips and tricks needed for website performance enhancement on the front-end side. There's no magic-bullet solution to solve all performance problems but I've collected information from multiple sources under one roof to help you with the process. Not all of these techniques may apply to your website so choose what works best for your needs. Refer the testing tools and resources section for more information.
Test Your Website:
Not all sites have to score a perfect 100. It all depends upon how your site is being used and where the performance matters.

{ } JavaScript Optimizations

Minify, Combine and Gzip (Compress) your code. Inline critical JavaScript and lazy-load the rest.

Minify JavaScript

Benefits: reducing network latency, enhancing compression, and faster browser loading and execution.

Combine JavaScript

Combining external scripts into as few files as possible cuts down on round-trip time (RTT) and delays in downloading other resources.

PageSpeed Module, Minify for PHP, YUI for Java

With the advent of HTTP 2.0 protocol, this process will have to be reversed for optimization because of request-response multiplexing.

Compress JavaScript / Gzip

Compressing resources with gzip or deflate can reduce the number of bytes sent over the network. Most modern browsers support data compression for HTML, CSS, and JavaScript files.

Click here for an example on how to use gzip compression on Apache Servers.

Don't use gzip for image or other binary files: Image file formats supported by the web, as well as videos, PDFs and other binary formats, are already compressed; using gzip on them won't provide any additional benefit, and can actually make them larger.

Use CDN Hosted Libraries

Benefits: decreased latency, increased parallelism, better caching, high availability and scalability.

Don't forget to implement a fallback for your CDN hosted libraries in case the CDN goes offline. Example:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/js/jquery-1.11.0.min.js"><\/script>')</script>

Inline Critical JavaScript

If you do have some critical JavaScript code to modify DOM initially (not very common) or if you have a lazy loading code, inline the JavaScript at the bottom of your page to avoid some round-trip time (RTT).

Defer loading

Deferring loading of JavaScript functions that are not called at startup reduces the initial download size, allowing other resources to be downloaded in parallel, and speeding up execution and rendering time. Here are a few options to defer the loading of JavaScript on your page:

  • Use defer attribute on script elements to indicate to a browser that the script is meant to be executed after the document has been parsed. This will not stop the browser from downloading the code, so better use the lazy loading techniques given below.
    <script type="text/javascript" src="your_script.js" defer></script>
    The defer attribute is only for external scripts (should only be used if the src attribute is present).
    Be careful when using the defer attribute on core libraries like jQuery. It might cause errors if the code relying upon a core library executes before it's loaded.
  • Manually injecting Scripts with callbacks, it makes sure the script is loaded before we start using it.
    function loadScript(url, callback) {
    	var script = document.createElement("script")
    	script.type = "text/javascript";
    	if (script.readyState) { //IE
    		script.onreadystatechange = function () {
    			if (script.readyState == "loaded" ||
    				script.readyState == "complete") {
    				script.onreadystatechange = null;
    				callback();
    			}
    		};
    	} else { //Others
    		script.onload = function () {
    			callback();
    		};
    	}
    	script.src = url;
    	document.getElementsByTagName("head")[0].appendChild(script);
    }
    
    loadScript("the-rest.js", function () {
    	Application.init();
    });
  • XMLHttpRequest Script Injection
    var xhr = new XMLHttpRequest();
    xhr.open("get", "file1.js", true);
    xhr.onreadystatechange = function () {
    	if (xhr.readyState == 4) {
    		if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
    			var script = document.createElement("script");
    			script.type = "text/javascript";
    			script.text = xhr.responseText;
    			document.body.appendChild(script);
    		}
    	}
    };
    xhr.send(null);
  • Use LazyLoad library to load external JavaScript and CSS files on demand.
    // Load single or multiple JS files and execute a callback when they've all finished.
    LazyLoad.js(['foo.js', 'bar.js', 'baz.js'], function () {
    	alert('all files have been loaded');
    });
  • jQuery users can simply use jQuery.getScript() method to lazy load and execute scripts on demand.
    $.getScript("foo.js")
    	.done(function (script, textStatus) { ... })
    	.fail(function (jqxhr, settings, exception) { ... });

Defer parsing

In order to load a page, the browser must parse the contents of all <script> tags, which adds additional time to the page load. By minimizing the amount of JavaScript needed to render the page, and deferring parsing of unneeded JavaScript until it needs to be executed, you can reduce the initial load time of your page.

  • The simplest and preferred technique is to simply Defer loading of JavaScript as described in the step above.
  • Use HTML5 async attribute on script elements to indicate that the browser should, if possible, execute the script asynchronously.
    <script type="text/javascript" src="your_script.js" async></script>
    The async attribute is only for external scripts (should only be used if the src attribute is present).
  • Load the JavaScript code inside comments so it doesn't get parsed/executed immediately and later use eval() method to execute it on demand. Make sure you strip out (or replace) comment blocks in your JavaScript first. The only saving here is that you are not making one extra round-trip to get the JavaScript code and on the contrary if your JavaScript code is too big, you are increasing your initial payload. You should prefer the deferred loading techniques mentioned above over this. More Info
    <html>
    ...
    <script id="lazy">
    /*
    alert("Hello World! I am feeling Lazy.");
    */
    </script>
    
    <script>
    	function lazyLoad(id) {
    		var lazyElement = document.getElementById(id);
    		var lazyElementBody = lazyElement.innerHTML;
    		var jsCode = lazyElementBody.replace(/^[\s\xA0]+\/\*|\*\/[\s\xA0]+$/g, "");
    		eval(jsCode);
    	}
    </script>
    
    <div onclick="lazyLoad('lazy')">Lazy Load</div>
    
    </html>
    The biggest pitfall to this technique is that you cannot debug the code executed by eval().

Leverage browser caching

Setting an expiry date or a maximum age in the HTTP headers for static resources instructs the browser to load previously downloaded resources from local disk rather than over the network.

HTTP/1.1 provides the following caching response headers:
  • Expires and Cache-Control: max-age. These specify the “freshness lifetime” of a resource, that is, the time period during which the browser can use the cached resource without checking to see if a new version is available from the web server.
  • Last-Modified and ETag. These specify some characteristic about the resource that the browser checks to determine if the files are the same. In the Last-Modified header, this is always a date. In the ETag header, this can be any value that uniquely identifies a resource (file versions or content hashes are typical).
It is important to specify one of Expires or Cache-Control max-age, and one of Last-Modified or ETag, for all cacheable resources. It is redundant to specify both Expires and Cache-Control: max-age, or to specify both Last-Modified and ETag.

Click here for an example on how to disable ETags and enable Expires headers on Apache Servers.

Leverage proxy caching

Enabling public caching in the HTTP headers for static resources allows the browser to download resources from a nearby proxy server rather than from a remote origin server.

You use the Cache-control: public header to indicate that a resource can be cached by public web proxies in addition to the browser that issued the request. With some exceptions (described below), you should configure your web server to set this header to public for cacheable resources.

Recommendations:

  • Don't include a query string in the URL for static resources.
  • Don't enable proxy caching for resources that set cookies.
  • Some public proxies have bugs that do not detect the presence of the Content-Encoding response header. This can result in compressed versions being delivered to client browsers that cannot properly decompress the files.

Place JS at the end of page.

Because JavaScript code can alter the content and layout of a web page, the browser delays rendering any content that follows a script tag until that script has been downloaded, parsed and executed. It's advised to put JavaScript just before the closing </body> tag on your page. A sample from HTML5 Boilerplate.

One benefit to this approach is that you don't have to attach DOM load events because the DOM is already loaded when you reach the end of </body> tag.

Use file/module loaders.

Use a file and module loader like RequireJS to load your script modules asynchronously with more control.

requirejs.config({
	enforceDefine: true,
	paths: {
		jquery: [
			'//ajax.aspnetcdn.com/ajax/jquery/jquery-1.11.0.min',
			//If the CDN location fails, load from this location
			'js/jquery-1.11.0.min'
		]
	}
});
 
//Later
require(['jquery'], function ($) {
});
JavaScript file and module loaders: RequireJS, curl.js, yepnope, HeadJS, LABjs, LazyLoad

Don't mix markup and behavior.

Keeping the behavior logic outside of HTML will help you optimize the deferred loading of JavaScript code.

CSS Optimizations

Minify, Combine and Gzip (Compress) your code. Inline critical CSS in the head and lazy-load rest of the CSS styles.

Minify CSS

Benefits: reducing network latency, enhancing compression, and faster browser loading and execution.

Compress CSS / Gzip

Compressing resources with gzip or deflate can reduce the number of bytes sent over the network. Most modern browsers support data compression for HTML, CSS, and JavaScript files.

Click here for an example on how to use gzip compression on Apache Servers.

Don't use gzip for image or other binary files: Image file formats supported by the web, as well as videos, PDFs and other binary formats, are already compressed; using gzip on them won't provide any additional benefit, and can actually make them larger.

Combine CSS

Combining external scripts into as few files as possible cuts down on round-trip time (RTT) and delays in downloading other resources.

PageSpeed Module, Minify for PHP, YUI for Java

Try to keep only one external CSS include on a page. If your CSS code gets bigger, try the Critical CSS approach listed below and lazy-load the lower priority code.

With the advent of HTTP 2.0 protocol, this process will have to be reversed for optimization because of request-response multiplexing.

Inline Critical CSS

Usually you only need a small amount of CSS code for above-the-fold content or the basic structure of the page. It's always better to strip out the critical/essential CSS code and inline it in the page head and lazy load rest of the CSS. css-tricks.com has more details on this.

This is the best approach if you have single-page apps and you don't need to reload the same CSS again on a different page.

You may not want to inline all your CSS because that will increase the size of your page and you don't get the benefits of Browser and Edge Caching.

Defer Loading

After inlining all your important CSS, you can lazy-load rest of your styles.

Use LazyLoad library to load remaining CSS files on demand.
LazyLoad.css(['remaining-styles.css', 'extra-styles.css'], function () {
	alert('all CSS files have been loaded');
});
Or inject the stylesheet using this small script...
function loadCSS(href, media){
	var ss = window.document.createElement('link'),
		ref = window.document.getElementsByTagName('head')[0];

	ss.rel = 'stylesheet';
	ss.href = href;

	// temporarily, set media to something non-matching to ensure it'll fetch without blocking render
	ss.media = 'only x';

	ref.parentNode.insertBefore(ss, ref);

	setTimeout( function(){
	  // set media back so that the stylesheet applies once it loads
	  ss.media = media || 'all';
	}, 0);
}

loadCss('things.css');

Avoid different stylesheets for media types

Don't use different includes for media types and combine them into one script, example: screen, print or the latest CSS3 media types. Most browsers will download your CSS no matter what media type your specify. More Information

<link rel="stylesheet" href="screen-styles.css" media="screen"/>
<link rel="stylesheet" href="print-styles.css" media="print"/> <!-- This CSS will load even for non-print view -->
<link rel="stylesheet" href="mobile-styles.css" media="(max-width: 480px)"/> <!-- This CSS will load even for screen sizes greater than 480 -->

Avoid CSS @import

CSS @import allows stylesheets to import other stylesheets. When CSS @import is used from an external stylesheet, the browser is unable to download the stylesheets in parallel, which adds additional round-trip times to the overall page load. Instead of @import, use a tag for each stylesheet. This allows the browser to download stylesheets in parallel, which results in faster page load times.

Put CSS in the document head

Moving inline style blocks and <link> elements from the document body to the document head improves rendering performance. It's important to put references to external stylesheets, as well as inline style blocks, in the head of the page.

Order of styles and scripts

Correctly ordering external stylesheets and external and inline scripts enables better parallelization of downloads and speeds up browser rendering time. Browsers delays rendering any content that follows a script tag until that script has been downloaded, parsed and executed. Stylesheets when placed before script tags will be downloaded in parallel.

Remove unused CSS

Removing or deferring style rules that are not used by a document avoid downloads unnecessary bytes and allow the browser to start rendering sooner.

uncss, Unused-CSS

Use CSS preprocessors

Using CSS preprocessors will help you in writing optimized CSS code.

SASS, LESS, Stylus, Myth

Image Optimizations

Reduce Image Size, use CSS Sprites and Lazy-Load when needed.

Compress

Properly formatting and compressing images can save many bytes of data. You can perform basic optimization with any image editing program, such as GIMP and Paint.NET.

Several tools are available that perform further, lossless compression on JPEG and PNG files, with no effect on image quality:
JPEG: jpegtran or jpegoptim (available on Linux only; run with the --strip-all option)
PNG: OptiPNG, PNGGauntlet, PNGOUT
Online Tools: PNG Crush, JpegMini, JPG Optimiser
Sample jpegtran command line optimization:
$ jpegtran -optimize -progressive filename.jpg > filename.jpg-opt
$ cp filename.jpg-opt filename.jpg
$ rm filename.jpg-opt

Combine images using CSS sprites

Combining images into as few files as possible using CSS sprites reduces the number of round-trips and delays in downloading other resources, reduces request overhead, and can reduce the total number of bytes downloaded by a web page.

Lazy-Load Images

Lazy loading the images which are not in users viewport will help you reduce initial page load time.

Use plugin like jQuery Lazyload:
<img class="lazy" src="spinner.gif" data-original="img/example.jpg" width="640" height="480">
$("img.lazy").lazyload({
	threshold : 200 // causes image to load 200 pixels before it appears on viewport.
});

Image Data URI

For small images, use Data URIs to embed the image directly in CSS code.

Conversion Tools: Data URI Convertor, Data Url Maker

Data URI Format:

data:[<mime type>][;charset=<charset>][;base64],<encoded data>
CSS Example:
li { 
  background-image: url(data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchP...);
}
HTML Example:
<img width="16" height="16" alt="star" src="data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGol..." />

Important Notes:

  • Size of embedded code is somewhat larger than size of resource by itself. GZip compression will help.
  • IE8 has the lowest maximum data URI size of 32768 Bytes.
  • Using PHP you can easily create Data URIs:
    <?php
    $filePath = "../image.gif";
    $mime = mime_content_type($filePath);
    $base64 = base64_encode(file_get_contents($filePath));
    echo "data:${mime};base64,${base64}";
    ?>

Combine images using Base64 sprites

For loading multiple images in a dynamic environment where it's not feasable to generate CSS Sprites, you can convert the images to Data URI/Base64 encoding and transfer in a batch using text or JSON format. Live Demo

Sample JSON to batch:
[ {"id":1, "url":"..." "base64": "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSK..."},
{"id":2, "url":"...", "base64": "data:image/gif;base64,ASDFSDGFERWEWFCVDFGERWEGD..."},
{"id":3, "url":"...", "base64": "data:image/gif;base64,SDFASRWEFSDFSAWRFSDGFSDDF..."} ]

Serve scaled images

To display the same image in various sizes, create thumbnails using image editing software so you can save bandwidth while serving the images.

Specify dimensions

Specifying a width and height for all images allows for faster rendering by eliminating the need for unnecessary reflows and repaints. To prevent reflows, specify the width and height of all images, either in the HTML <img> tag, or in CSS.

Don't Scale Images in HTML

Don't use a bigger image than you need just because you can set the width and height in HTML.

Avoid Empty Image src

For every img tag with empty src attribute, some browsers make another request to your server. More Info

  • Internet Explorer makes a request to the directory in which the page is located.
  • Safari and Chrome make a request to the actual page itself.
  • Firefox 3 and earlier versions behave the same as Safari and Chrome, but version 3.5 addressed this issue and no longer sends a request.
  • Opera does not do anything when an empty image src is encountered.

Progressive JPEGs

Jpegs come in two flavors: baseline and progressive. A baseline jpeg is a full-resolution top-to-bottom scan of the image, and a progressive jpeg is a series of scans of increasing quality. More Info

You can use jpegtran as shown above to compress JPEGs progressively.

Icon Fonts

Icon fonts are the best additions to modern web design. If you are using a lot of icons on your site, this is the way to go! Icon fonts are scalable and you can easily change their colors like regular fonts on your page. Font Icon payload is very low compared to good old GIFs and PNGs.

Some popular fonts: Font-Awesome (used on this page), Entypo, Typicons, Iconic, Modern Pictograms, Meteocons, Meteocons, MFG-Labs, Linecons, Web-Symbols. You can use Fontello to piece together your own icon font based on icons from a number of sources.

HTML Optimizations

Minify HTML, use Gzip (compression) and Lazy-Load when possible.

Minify HTML

Removing extra line breaks and comments can reduce initial page load time. For attribute values without spaces, you can skip quotes around them to save a few bytes.

Compress HTML

Compressing resources with gzip or deflate can reduce the number of bytes sent over the network. Most modern browsers support data compression for HTML, CSS and JavaScript files.

Click here for an example on how to use gzip compression on Apache Servers.

Reduce the Number of DOM Elements

Write better and more semantically correct markup. More DOM Elements means slower DOM access in JavaScript.

Defer Loading

Keep you HTML modular and lazy-load it to minimize intial page payload. You can have empty containers like div/iframe and lazy-load the content via AJAX once the container comes into the viewport.

Use plugin like jQuery Waypoints to lazy-load HTML blocks:
<div class="lazy" data-url="lazy.html"></div>
$('.lazy').waypoint(function() {
	var $this = $(this);
	$this.load($this.data("url"));
}, { offset: '25%' }); // tells Waypoints how far from the top of the window the callback should fire

Content required for SEO should not be lazy loaded.

Optimize Page Repaint

Optimizing the way you manipulate the DOM using JavaScript can reduce page repaint time.

Miscellaneous

Avoid bad requests

Removing "broken links", or requests that result in 404/410 errors, avoids wasteful requests.

Minimize HTTP Requests

Concatenate CSS and JavaScript and use image sprites to reduce additional round-trip time (RTT).

Minimize request size

Keeping cookies and request headers as small as possible ensures that an HTTP request can fit into a single packet.

Minimize redirects

Minimizing HTTP redirects from one URL to another cuts out additional round-trip time (RTT) and wait time for users.

Reduce DNS Lookups

For every new server the DNS resolver is contacted by the browser which returns that server's IP address. It typically takes 20-120 milliseconds for DNS to lookup the IP address for a given hostname. The browser can't download anything from this hostname until the DNS lookup is completed.

Use Domain Sharding

Serving resources from two different hostnames/domains increases parallelization of downloads. Doing this allows more resources to be downloaded in parallel, reducing the overall page load time. Make sure you're using not more than 2-4 domains because of the DNS lookup penalty. More Info

Reduce Cookie Size

Cookies are sent to the server with every request you make so if you have a lot of cookies or cookies with large size, your total request size will increase. More Info

  • Eliminate unnecessary cookies
  • Keep cookie sizes as low as possible to minimize the impact on the user response time
  • Be mindful of setting cookies at the appropriate domain level so other sub-domains are not affected
  • Set an Expires date appropriately. An earlier Expires date or none removes the cookie sooner, improving the user response time

Cache Stagnant Data

If you have data that does not change often, try caching it on the client to reduce server calls. You can use simple JavaScript object caching (good for single page apps) for dynamic apps (example: a small autocomplete list) or use HTML5 SessionStorage or LocalStorage APIs for long term storage.

Use GET for AJAX Requests

When using XMLHttpRequest (AJAX), POST is implemented in the browsers as a two-step process: sending the headers first, then sending data. So it's best to use GET, which only takes one TCP packet to send (unless you have a lot of cookies). The maximum URL length in IE is 2K, so if you send more than 2K data you might not be able to use GET.

Serve resources from a consistent URL

It's important to serve a resource from a unique URL, to eliminate duplicate download bytes and additional RTTs.

Improve server response time

Optimize your back-end code to respond faster. Your server needs to begin sending the first byte of the resource within 200ms of the request being sent.

Specify a viewport for mobile browsers

If your page has been designed with mobile devices in mind, then you should utilize the viewport meta tag so that mobile devices can render the document at the correct size.

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

Serve static content from a cookie-less domain

Serving static resources from a cookieless domain reduces the total size of requests made for a page. In Apache you can set or unset headers before requests are answered and block any front-end cookie setters like Google Analytics. More Info

Header unset Cookie
Header unset Set-Cookie

Specify a character set

Specifying a character set in the HTTP response headers of your HTML documents allows the browser to begin parsing HTML and executing scripts immediately. Apache .htaccess setting for UTF-8:

# Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
AddDefaultCharset utf-8

# Force UTF-8 for certain file formats.
<IfModule mod_mime.c>
	AddCharset utf-8 .atom .css .js .json .jsonld .rss .vtt .webapp .xml
</IfModule>

Tools to test you website performance

Resources