Tuesday, September 23, 2014

High Performance Web Sites - Best Practices

Client Side Performance - Best Practices

80% of the end-user response time is spent on the front-end.Most of this time is tied up in downloading all the components in the page: images, stylesheets, scripts, Flash, etc. This document contains a number of best practice rules for making web pages fast. Most of the suggestions improve the performance from the client side i.e. once html is downloaded.

Best Practices

Here are best practices segregated into different categories.

1. Content

1.1 Make Fewer HTTP Requests

Reducing the number of components in turn reduces the number of HTTP requests required to render the page.
  • Combined files are a way to reduce the number of HTTP requests by combining all scripts into a single script, and similarly combining all CSS into a single stylesheet. Combining files is more challenging when the scripts and stylesheets vary from page to page, but making this part of your release process improves response times.
  • CSS Sprites are the preferred method for reducing the number of image requests. Combine your background images into a single image and use the CSS background-image and background-position properties to display the desired image segment.
  • Inline images use the data: URL scheme to embed the image data in the actual page. This can increase the size of your HTML document. Combining inline images into your (cached) stylesheets is a way to reduce HTTP requests and avoid increasing the size of your pages. Inline images are not yet supported across all major browsers.

1.2 Reduce DNS Lookups

The Domain Name System (DNS) maps hostnames to IP addresses. Every DNS lookup 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.
  • Reducing the number of unique hostnames has the potential to reduce the amount of parallel downloading that takes place in the page.
  • Caution : Avoiding DNS lookups cuts response times, but reducing parallel downloads may increase response times.
  • Guideline is to split these components across at least two but no more than four hostnames

1.3 Make Ajax Cacheable

One of the cited benefits of Ajax is that it provides instantaneous feedback to the user because it requests information asynchronously from the backend web server
  • Add an Expires or a Cache-Control Header to make the responses cacheable to improve the performance of Ajax.

1.4 Postload Components

You can take a closer look at your page and ask yourself: "What's absolutely required in order to render the page initially?". The rest of the content and components can wait.
  • JavaScript is an ideal candidate for splitting before and after the onload event
    • Example if you have JavaScript code and libraries that do drag and drop and animations, those can wait, because dragging elements on the page comes after the initial rendering
  • Post-load hidden content e.g. that appears after a user action
  • Images below the fold (you have to scroll to see that)

1.5 Preload Components

Preload may look like the opposite of post-load, but it actually has a different goal.
  • Preload components when the browser is idle and request components (like images, styles and scripts) you'll need in the future. This way when the user visits the next page, you could have most of the components already in the cache and your page will load much faster for the user.
There are actually several types of preloading:
  1. Unconditional preload - as soon as onload fires, you go ahead and fetch some extra components.
  2. Conditional preload - based on a user action you make an educated guess where the user is headed next and preload accordingly.
  3. Anticipated preload - preload in advance before launching a redesign or pushing new Build into production

1.6 Reduce the Number of DOM Elements

It makes a difference if you loop through 500 or 5000 DOM elements on the page when you want to add an event handler. It Slows!
  • Check : Are you using nested tables for layout purposes? Are you throwing in more
    s only to fix layout issues?
  • The number of DOM elements is easy to test, just type in Firebug's console: document.getElementsByTagName('*').length
  • Even pretty busy page shouldn't have more than 800-900 DOM elements.

1.7 Split Components Across Domains

Splitting components allows you to maximize parallel downloads.
  • Use not more than 2-4 domains because of the DNS lookup penalty.
  • Example, you can host your HTML and dynamic content on www.wamart.com and split static components between i1.walmart.com and i2.walmart.com

1.8 Minimize Number of iframes

Iframes allow an HTML document to be inserted in the parent document. It's important to understand how iframes work so they can be used effectively.
  • iframe pros:
    1. Helps with slow third-party content like badges and ads
    2. Security sandbox
    3. Download scripts in parallel
  • iframe cons:
    1. Costly even if blank
    2. Blocks page onload
    3. Non-semantic

1.9 Avoid 404s

HTTP requests are expensive so making an HTTP request and getting a useless response (i.e. 404 Not Found) is totally unnecessary and will slow down the user experience without any benefit.
  • Even worse when the link to an external JavaScript is wrong and the result is a 404
    • First, this download will block parallel downloads.
    • Second, the browser may try to parse the 404 response body as if it were JavaScript code, trying to find something usable in it

1.10 Avoid Redirects

Redirects are accomplished using the 301 and 302 status codes.
  • Redirects slow down the user experience. Inserting a redirect between the user and the HTML document delays everything in the page since nothing in the page can be rendered and no components can start being downloaded until the HTML document has arrived.

2. Server

2.1 Use a CDN

The user's proximity to your web server has an impact on response times. A content delivery network (CDN) is a collection of web servers distributed across multiple locations to deliver content more efficiently to users.
  • It may improve the end-user response times by 20% or more

2.2 Add Expires or Cache-Control Header

A first-time visitor to your page may have to make several HTTP requests, but by using the Expires header you make those components cacheable.
  • This avoids unnecessary HTTP requests on subsequent page views.
  • Very simple to configure. E.g. in Apache httpd.conf ExpiresDefault "access plus 10 years"
  • The number of page views with a primed cache is 70-80% typically for a eCommerce site like walmart.com
  • Caution : if you use a far future Expires header you have to change the component's filename whenever the component changes e.g. use version like asda_v1.2.js

2.3 Gzip Components

The time it takes to transfer an HTTP request and response across the network can be significantly reduced by enabling compression for textual components ( e.g. JavaScripts, CSS, HTML)
  • Compression reduces response times by reducing the size of the HTTP response.
  • Starting with HTTP/1.1, web clients supports compression with the Accept-Encoding header in the HTTP request. Accept-Encoding: gzip, deflate
  • Gzipping generally reduces the response size by about 70%
  • Caution : Image and PDF files should not be gzipped because they are already compressed. Trying to gzip wastes CPU

2.4 Configure ETags

Entity tags (ETags) are a mechanism that web servers and browsers use to determine whether the component in the browser's cache matches the one on the origin server.
  • ETags won't match when a browser gets the original component from one server and later tries to validate that component on a different server, a too common situation on Web sites that use a cluster of servers to handle requests
  • If you have multiple servers hosting your web site, users may be getting slower pages, servers may have a higher load & consuming greater bandwidth, and proxies aren't caching the content efficiently.
    • Even if your components have a far future Expires header, a conditional GET request is still made whenever the user hits Reload or Refresh.
  • Very simple to configure. E.g. in Apache httpd.conf FileETag none

2.5 Flush Buffer Early

When users request a page, it can take anywhere from 200 to 500ms for the backend server to stitch together the HTML page and serve.
  • During this time, the browser is idle as it waits for the data to arrive.
  • Use some kind of flush buffer mechanism so that the browser can start fetching components while your backend is busy.
    • E.g. in PHP, flush() can be used.
  • A good place to consider flushing is right after the HEAD so that browser can fetch CSS & JavaScript files in parallel.

2.6 Use GET for Ajax Requests

When using XMLHttpRequest, POST is implemented in the browsers as a two-step process: sending the headers first, then sending data.
  • It's best to use GET, which only takes one TCP packet to send (unless you have a lot of cookies).
  • Also as per W3C standards, GET is meant for retrieving information, so it makes sense (semantically) to use GET when you're only requesting data, as opposed to sending data to be stored server-side.
  • Caution : The maximum URL length in IE is 2K, so if you send more than 2K data you might not be able to use GET.

2.7 Avoid Empty Image src

Without a purpose, browser makes another request to the server.
  • Browser beahavior:
    • 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[bug 444931] and no longer sends a request.
    • Opera does not do anything when an empty image src is encountered.
  • Possible reason, when creating the image dynamiccaly, and value is not available
    • E.g. Using Javascript, var img = new Image(); img.src = '';

3.1 Reduce Cookie Size

Information about cookies is exchanged in the HTTP headers between web servers and browsers. It's important to keep the size of cookies as low as possible to minimize the impact on the user's response time. Few suggestions:
  • 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

3.2 Use Cookie-Free Domains for Components

Serving static components usually doesn't require a cookie. Therefore, if static components get served from main domain e.g. walmart.com, it will carry unnecessary load of cookies.
  • Serve components for other domains e.g. i1.walmart.com, or better to be i1.wmcdn.com similar to YouTube which uses ytimg.com, or Amazon uses images-amazon.com

4. CSS

4.1 Put Stylesheets at Top

Moving stylesheets to the document HEAD makes pages appear to be loading faster. This is because putting stylesheets in the HEAD allows the page to render progressively.
  • Problem with putting stylesheets near the bottom of the document is that
    1. it prohibits progressive rendering in many browsers, including Internet Explorer.
    2. These browsers block rendering to avoid having to redraw elements of the page if their styles change. The user may stuck sometimes viewing a blank white page.

4.2 Avoid CSS Expressions

CSS expressions are a powerful (and dangerous) way to set CSS properties dynamically.
  • Example : background color could be set to alternate every hour using CSS expressions: background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );
  • Problem with expressions is that they are evaluated more frequently than most people expect.
    1. They get evaluated when the page is rendered or resized or scrolled or even when the user moves the mouse over the pag
    2. Moving the mouse around the page can easily generate more than 10,000 evaluations.

4.3 Choose Over @import

One of the previous best practices states that CSS should be at the top in order to allow for progressive rendering. However, @import doesn't behave the same as does.
  • Problem : In IE @import behaves the same as using at the bottom of the page, so it's best not to use it.

4.4 Avoid Filters

The IE-proprietary AlphaImageLoader filter aims to fix a problem with semi-transparent true color PNGs in IE versions < 7
  • Problem with this filter is that:
    1. it blocks rendering and freezes the browser while the image is being downloaded
    2. It also increases memory consumption and is applied per element, not per image, so the problem is multiplied.
  • The best approach is to avoid AlphaImageLoader completely and use gracefully degrading PNG8 instead, which are fine in IE. If you absolutely need AlphaImageLoader, use the underscore hack _filter as to not penalize your IE7+ users.

5. JavaScript

5.1 Put Scripts at Bottom

  • The problem caused by scripts is that they block parallel downloads
    • If a script is downloading, the browser won't start any other downloads, even on different hostnames.
  • If scripts can't be moved to lower of the page, use deferred scripts, which indicates that the script does not contain document.write, and is a clue to browsers to continue rendering
    • Unfortunately, Firefox doesn't support the DEFER attribute

5.2 Make JavaScript and CSS External

For eCommerce sites like walmart.com, HTML is always dynamic in nature. Javascipts & CSS contents being considered static instead.
  • Using external files in the real world generally produces faster pages because the JavaScript and CSS files are cached by the browser
  • JavaScript and CSS that are inlined in HTML documents get downloaded every time the HTML document is requested, resulting in
    1. Increased HTML size which can't be cached
    2. Increased wait time due to rendering can not be started until HTML get downloaded.
    3. Probable Increased wight as it can't be uses on other pages even if required, so need to download again.

5.3 Minify JavaScript and CSS

Minification is the practice of removing unnecessary characters from code to reduce its size thereby improving load times.
  • Minification removes all comments & unneeded white space characters (space, newline, and tab)
  • Further Obfuscation can be applied within minification, to reduce size further.
  • In a survey of ten top U.S. web sites, minification achieved a 21% size reduction (25% with obfuscation)
  • E.g. use YUI Compressor  
    • java -jar yuicompressor3..0.jar -type [js | css] [filename] -o [output filename]

5.4 Remove Duplicate Scripts

It hurts performance to include the same JavaScript file twice in one page and sad part is this isn't as unusual.
  • E.g. Some markets having 3 duplicates of jquery.js being downloaded thru different URLs and one with different version (served one from each http, https & google).

5.5 Minimize DOM Access

Accessing DOM elements with JavaScript is slow so in order to have a more responsive page, you should:
  • Cache references to accessed elements
  • Update nodes "offline" and then add them to the tree
  • Avoid fixing layout with JavaScript

5.6 Develop Smart Event Handlers

Sometimes pages feel less responsive because of too many event handlers attached to different elements of the DOM tree which are then executed too often
  • Use event delegation e.g. If you have 10 buttons inside a div, attach only one event handler to the div wrapper, instead of one handler for each butto

6. Images

6.1 Optimize Images

Optimize images by compressing them in lossless fashion (without compromising any visual quality) to reduce page weight.
  • Use Smushit or ImageMagick for lossless compression.

6.2 Optimize CSS Sprites

  • Don't leave big gaps between the images in a sprite.
  • Combining similar colors in a sprite helps you keep the color count low, ideally under 256 colors so to fit in a PNG8.
  • Arranging the images in the sprite horizontally as opposed to vertically usually results in a smaller file size.

6.3 Do Not Scale Images in HTML

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

6.4 Make favicon.ico Small and Cacheable

  • Since it's on the same server, cookies are sent every time it's requested
  • It also interferes with the download sequence, e.g. in IE when you request extra components in the onload, the favicon will be downloaded before these extra components.
  • It should be small, preferably under 1K.
  • Set Expires header with what you feel comfortable since you cannot rename it if you decide to change it.

7. Mobile

7.1 Keep Components Under 25 KB

This restriction is related to the fact that iPhone won't cache components bigger than 25K. Note that this is the uncompressed size. This is where minification is important because gzip alone may not be sufficient.

7.2 Pack Components Into a Multipart Document

Packing components into a multipart document is like an email with attachments, it helps you fetch several components with one HTTP request (remember: HTTP requests are expensive).
  • Caution : When you use this technique, first check if the user agent supports it (iPhone does not).

Performance Tools

Note : Getting a good grade doesn't mean good performance, rather it tells that standards are being followed for better performance.

YSlow - The Tool

YSlow tool is used to analyze a web pages and get a report on why the web page is slow based on the best practices for high performance web sites. YSlow is a Firefox add-on integrated with the Firebug web development tool, and Grade pages on the basis of certain performance rules.

Google Page Speed

Page sppeed is similar to YSlow, except few more suggestions regarding client side performance optimizations.

Advanced (more)

Useful Reads

No comments:


Related Posts with Thumbnails