-
- Weitere Informationen zu diesem Buch:
Inhaltsverzeichnis | Index | Kolophon |
- Weitere Informationen zu diesem Buch:
JETZT ONLINE BESTELLEN
Essential Knowledge for Front-End Engineers
First Edition September 2007
ISBN 978-0-596-52930-7
168 Seiten
EUR24.50
Weitere Informationen zu diesem Buch
Inhaltsverzeichnis | Index | Kolophon |
Inhaltsverzeichnis
- Chapter 1: The Importance of Frontend Performance
- InhaltsvorschauMost of my web career has been spent as a backend engineer. As such, I dutifully approached each performance project as an exercise in backend optimization, concentrating on compiler options, database indexes, memory management, etc. There's a lot of attention and many books devoted to optimizing performance in these areas, so that's where most people spend time looking for improvements. In reality, for most web pages, less than 10–20% of the end user response time is spent getting the HTML document from the web server to the browser. If you want to dramatically reduce the response times of your web pages, you have to focus on the other 80–90% of the end user experience. What is that 80–90% spent on? How can it be reduced? The chapters that follow lay the groundwork for understanding today's web pages and provide 14 rules for making them faster.In order to know what to improve, we need to know where the user spends her time waiting. shows the HTTP traffic when Yahoo!'s home page (http://www.yahoo.com) is downloaded using Internet Explorer. Each bar is one HTTP request. The first bar, labeled
html, is the initial request for the HTML document. The browser parses the HTML and starts downloading the components in the page. In this case, the browser's cache was empty, so all of the components had to be downloaded. The HTML document is only 5% of the total response time. The user spends most of the other 95% waiting for the components to download; she also spends a small amount of time waiting for HTML, scripts, and stylesheets to be parsed, as shown by the blank gaps between downloads.
Figure : Downloading http://www.yahoo.com in Internet Explorer, empty cacheshows the same URL downloaded in Internet Explorer a second time. The HTML document is only 12% of the total response time. Most of the components don't have to be downloaded because they're already in the browser's cache.
Figure : Downloading http://www.yahoo.com in Internet Explorer, primed cacheFive components are requested in this second page view:- One redirect
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Tracking Web Page Performance
- InhaltsvorschauIn order to know what to improve, we need to know where the user spends her time waiting. shows the HTTP traffic when Yahoo!'s home page (http://www.yahoo.com) is downloaded using Internet Explorer. Each bar is one HTTP request. The first bar, labeled
html, is the initial request for the HTML document. The browser parses the HTML and starts downloading the components in the page. In this case, the browser's cache was empty, so all of the components had to be downloaded. The HTML document is only 5% of the total response time. The user spends most of the other 95% waiting for the components to download; she also spends a small amount of time waiting for HTML, scripts, and stylesheets to be parsed, as shown by the blank gaps between downloads.
Figure : Downloading http://www.yahoo.com in Internet Explorer, empty cacheshows the same URL downloaded in Internet Explorer a second time. The HTML document is only 12% of the total response time. Most of the components don't have to be downloaded because they're already in the browser's cache.
Figure : Downloading http://www.yahoo.com in Internet Explorer, primed cacheFive components are requested in this second page view:- One redirect
- This redirect was downloaded previously, but the browser is requesting it again. The HTTP response's status code is 302 ("Found" or "moved temporarily") and there is no caching information in the response headers, so the browser can't cache the response. I'll discuss HTTP in .
- Three uncached images
- The next three requests are for images that were not downloaded in the initial page view. These are images for news photos and ads that change frequently.
- One cached image
- The last HTTP request is a conditional GETrequest. The image is cached, but because of the HTTP response headers, the browser has to check that the image is up-to-date before showing it to the user. Conditional GET requests are also described in .
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Where Does the Time Go?
- InhaltsvorschauLooking at the HTTP traffic in this way, we see that at least 80% of the end user response time is spent on the components in the page. If we dig deeper into the details of these charts, we start to see how complex the interplay between browsers and HTTP becomes. Earlier, I mentioned how the HTTP status codes and headers affect the browser's cache. In addition, we can make these observations:
- The cached scenario () doesn't have as much download activity. Instead, you can see a blank space with no downloads that occurs immediately following the HTML document's HTTP request. This is time when the browser is parsing HTML, JavaScript, and CSS, and retrieving components from its cache.
- Varying numbers of HTTP requests occur in parallel. has a maximum of three HTTP requests happening in parallel, whereas in , there are as many as six or seven simultaneous HTTP requests. This behavior is due to the number of different hostnames being used, and whether they use HTTP/1.0 or HTTP/1.1. explains these issues in the section "."
- Parallel requests don't happen during requests for scripts. That's because in most situations, browsers block additional HTTP requests while they download scripts. See to understand why this happens and how to use this knowledge to improve page load times.
Figuring out exactly where the time goes is a challenge. But it's easy to see where the time does not go—it does not go into downloading the HTML document, including any backend processing. That's why frontend performance is important.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Performance Golden Rule
- InhaltsvorschauThis phenomenon of spending only 10–20% of the response time downloading the HTML document is not isolated to Yahoo!'s home page. This statistic holds true for all of the Yahoo! properties I've analyzed (except for Yahoo! Search because of the small number of components in the page). Furthermore, this statistic is true across most web sites. shows 10 top U.S. web sites extracted from http://www.alexa.com. Note that all of these except AOL were in the top 10 U.S. web sites. Craigslist.org was in the top 10, but its pages have little to no images, scripts, and stylesheets, and thus was a poor example to use. So, I chose to include AOL in its place.
Table : Percentage of time spent downloading the HTML document for 10 top web sites Empty cachePrimed cacheAOL6%14%Amazon18%14%CNN19%8%eBay2%8%Google14%36%MSN3%5%MySpace4%14%Wikipedia20%12%Yahoo!5%12%YouTube3%5%All of these web sites spend less than 20% of the total response time retrieving the HTML document. The one exception is Google in the primed cache scenario. This is because http://www.google.com had only six components, and all but one were configured to be cached by the browser. On subsequent page views, with all those components cached, the only HTTP requests were for the HTML document and an image beacon.In any optimization effort, it's critical to profile current performance to identify where you can achieve the greatest improvements. It's clear that the place to focus is frontend performance.First, there is more potential for improvement in focusing on the frontend. If we were able to cut backend response times in half, the end user response time would decrease only 5–10% overall. If, instead, we reduce the frontend performance by half, we would reduce overall response times by 40–45%.Second, frontend improvements typically require less time and fewer resources. Reducing backend latency involves projects such as redesigning application architecture and code, finding and optimizing critical code paths, adding or modifying hardware, distributing databases, etc. These projects take weeks or months. Most of the frontend performance improvements described in the following chapters involve best practices, such as changing web server configuration files ( and ); placing scripts and stylesheets in certain places within the page ( and ); and combining images, scripts, and stylesheets (). These projects take hours or days—much less than the time required for most backend improvements.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 2: HTTP Overview
- InhaltsvorschauBefore diving into the specific rules for making web pages faster, it's important to understand the parts of the HyperText Transfer Protocol (HTTP) that affect performance. HTTP is how browsers and servers communicate with each other over the Internet. The HTTP specification was coordinated by the World Wide Web Consortium (W3C) and Internet Engineering Task Force (IETF), resulting in RFC 2616. HTTP/1.1 is the most common version today, but some browsers and servers still use HTTP/1.0.HTTP is a client/server protocol made up of requests and responses. A browser sends an HTTP request for a specific URL, and a server hosting that URL sends back an HTTP response. Like many Internet services, the protocol uses a simple, plaintext format. The types of requests are GET, POST, HEAD, PUT, DELETE, OPTIONS, and TRACE. I'm going to focus on the GET request, which is the most common.A GET request includes a URL followed by headers. The HTTP response contains a status code, headers, and a body. The following example shows the possible HTTP headers when requesting the script yahoo_2.0.0-b2.js.
GET /us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js HTTP/1.1 Host: us.js2.yimg.com User-Agent: Mozilla/5.0 (...) Gecko/20061206 Firefox/1.5.0.9 HTTP/1.1 200 OK Content-Type: application/x-javascript Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT Content-Length: 355 var YAHOO=...
The size of the response is reduced using compression if both the browser and server support it. Browsers announce their support of compression using theAccept-Encodingheader. Servers identify compressed responses using theContent-Encodingheader.GET /us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js HTTP/1.1 Host: us.js2.yimg.com User-Agent: Mozilla/5.0 (...) Gecko/20061206 Firefox/1.5.0.9 Accept-Encoding: gzip,deflate HTTP/1.1 200 OK Content-Type: application/x-javascript Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT Content-Length: 255 Content-Encoding: gzip ^_\;213^H^@^@^@^@^@^@^Cl\217\315j\3030^P\204_E\361IJ...
Notice how the body of the response is compressed. explains how to turn on compression, and warns about edge cases that can arise due to proxy caching. TheEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Compression
- InhaltsvorschauThe size of the response is reduced using compression if both the browser and server support it. Browsers announce their support of compression using the
Accept-Encodingheader. Servers identify compressed responses using theContent-Encodingheader.GET /us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js HTTP/1.1 Host: us.js2.yimg.com User-Agent: Mozilla/5.0 (...) Gecko/20061206 Firefox/1.5.0.9 Accept-Encoding: gzip,deflate HTTP/1.1 200 OK Content-Type: application/x-javascript Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT Content-Length: 255 Content-Encoding: gzip ^_\;213^H^@^@^@^@^@^@^Cl\217\315j\3030^P\204_E\361IJ...
Notice how the body of the response is compressed. explains how to turn on compression, and warns about edge cases that can arise due to proxy caching. TheVaryandCache-Controlheaders are also discussed.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conditional GET Requests
- InhaltsvorschauIf the browser has a copy of the component in its cache, but isn't sure whether it's still valid, a conditional GET request is made. If the cached copy is still valid, the browser uses the copy from its cache, resulting in a smaller response and a faster user experience.Typically, the validity of the cached copy is derived from the date it was last modified. The browser knows when the component was last modified based on the
Last-Modifiedheader in the response (refer to the previous sample responses). It uses theIf-Modified-Sinceheader to send the last modified date back to the server. The browser is essentially saying, "I have a version of this resource with the following last modified date. May I just use it?"GET /us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js HTTP/1.1 Host: us.js2.yimg.com User-Agent: Mozilla/5.0 (...) Gecko/20061206 Firefox/1.5.0.9 Accept-Encoding: gzip,deflate If-Modified-Since: Wed, 22 Feb 2006 04:15:54 GMT
HTTP/1.1 304 Not Modified Content-Type: application/x-javascript Last-Modified: Wed, 22 Feb 2006 04:15:54 GM
If the component has not been modified since the specified date, the server returns a "304 Not Modified" status code and skips sending the body of the response, resulting in a smaller and faster response. In HTTP/1.1 theETagandIf-None-Matchheaders are another way to make conditional GET requests. Both approaches are discussed in .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Expires
- InhaltsvorschauConditional GET requests and 304 responses help pages load faster, but they still require making a roundtrip between the client and server to perform the validity check. The
Expiresheader eliminates the need to check with the server by making it clear whether the browser can use its cached copy of a component.HTTP/1.1 200 OK Content-Type: application/x-javascript Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT Expires: Wed, 05 Oct 2016 19:16:20 GMT
When the browser sees anExpiresheader in the response, it saves the expiration date with the component in its cache. As long as the component hasn't expired, the browser uses the cached version and avoids making any HTTP requests. talks about theExpiresandCache-Controlheaders in more detail.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Keep-Alive
- InhaltsvorschauHTTP is built on top of Transmission Control Protocol (TCP). In early implementations of HTTP, each HTTP request required opening a new socket connection. This is inefficient because many HTTP requests in a web page go to the same server. For example, most requests for images in a web page all go to a common image server. Persistent Connections (also known as Keep-Alive in HTTP/1.0) was introduced to solve the inefficiency of opening and closing multiple socket connections to the same server. It lets browsers make multiple requests over a single connection. Browsers and servers use the
Connectionheader to indicate Keep-Alive support. TheConnectionheader looks the same in the server's response.GET /us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js HTTP/1.1 Host: us.js2.yimg.com User-Agent: Mozilla/5.0 (...) Gecko/20061206 Firefox/1.5.0.9 Accept-Encoding: gzip,deflate Connection: keep-alive HTTP/1.1 200 OK Content-Type: application/x-javascript Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT Connection: keep-alive
The browser or server can close the connection by sending aConnection: closeheader. Technically, theConnection: keep-aliveheader is not required in HTTP/1.1, but most browsers and servers still include it.Pipelining, defined in HTTP/1.1, allows for sending multiple requests over a single socket without waiting for a response. Pipelining has better performance than persistent connections. Unfortunately, pipelining is not supported in Internet Explorer (up to and including version 7), and it's turned off by default in Firefox through version 2. Until pipelining is more widely adopted, Keep-Alive is the way browsers and servers can more efficiently use socket connections for HTTP. This is even more important for HTTPS because establishing new secure socket connections is more time consuming.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - There's More
- InhaltsvorschauThis chapter contains just an overview of HTTP and focuses only on the aspects that affect performance. To learn more, read the HTTP specification (http://www.w3.org/protocols/rfc2616/rfc2616.html) and HTTP: The Definitive Guide by David Gourley and Brian Totty (O'Reilly; http://www.oreilly.com/catalog/httptdg). The parts highlighted here are sufficient for understanding the best practices described in the following chapters.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 3: Rule 1: Make Fewer HTTP Requests
- InhaltsvorschauThe Performance Golden Rule, as explained in , reveals that only 10–20% of the end user response time involves retrieving the requested HTML document. The remaining 80–90% of the time is spent making HTTP requests for all the components (images, scripts, stylesheets, Flash, etc.) referenced in the HTML document. Thus, a simple way to improve response time is to reduce the number of components, and, in turn, reduce the number of HTTP requests.Suggesting the idea of removing components from the page often creates tension between performance and product design. In this chapter, I describe techniques for eliminating HTTP requests while avoiding the difficult tradeoff decisions between performance and design. These techniques include using image maps, CSS sprites, inline images, and combined scripts and stylesheets. Using these techniques reduces response times of the example pages by as much as 50%.In its simplest form, a hyperlink associates the destination URL with some text. A prettier alternative is to associate the hyperlink with an image, for example in navbars and buttons. If you use multiple hyperlinked images in this way, image maps may be a way to reduce the number of HTTP requests without changing the page's look and feel. An image map allows you to associate multiple URLs with a single image. The destination URL is chosen based on where the user clicks on the image.shows an example of five images used in a navbar. Clicking on an image takes you to the associated link. This could be done with five separate hyperlinks, using five separate images. It's more efficient, however, to use an image map because this reduces the five HTTP requests to just one HTTP request. The response time is faster because there is less HTTP overhead.
Figure : Image map candidateYou can try this out for yourself by visiting the following URLs. Click on each link to see the roundtrip retrieval time.- No Image Map
- Image Map
When using Internet Explorer 6.0 over DSL (~900 Kbps), the image map retrieval was 56% fasterEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Image Maps
- InhaltsvorschauIn its simplest form, a hyperlink associates the destination URL with some text. A prettier alternative is to associate the hyperlink with an image, for example in navbars and buttons. If you use multiple hyperlinked images in this way, image maps may be a way to reduce the number of HTTP requests without changing the page's look and feel. An image map allows you to associate multiple URLs with a single image. The destination URL is chosen based on where the user clicks on the image.shows an example of five images used in a navbar. Clicking on an image takes you to the associated link. This could be done with five separate hyperlinks, using five separate images. It's more efficient, however, to use an image map because this reduces the five HTTP requests to just one HTTP request. The response time is faster because there is less HTTP overhead.
Figure : Image map candidateYou can try this out for yourself by visiting the following URLs. Click on each link to see the roundtrip retrieval time.- No Image Map
- Image Map
When using Internet Explorer 6.0 over DSL (~900 Kbps), the image map retrieval was 56% faster than the retrieval for the navbar with separate images for each hyperlink (354 milliseconds versus 799 milliseconds). That's because the image map has four fewer HTTP requests.There are two types of image maps. Server-side image maps submit all clicks to the same destination URL, passing along the x,y coordinates of where the user clicked. The web application maps the x,y coordinates to the appropriate action. Client-side image maps are more typical because they map the user's click to an action without requiring a backend application. The mapping is achieved via HTML'sMAPtag. The HTML for converting the navbar in to an image map shows how theMAPtag is used:<img usemap="#map1" border=0 src="/images/imagemap.gif"> <map name="map1"> <area shape="rect" coords="0,0,31,31" href="home.html" title="Home"> <area shape="rect" coords="36,0,66,31" href="gifts.html" title="Gifts"> <area shape="rect" coords="71,0,101,31" href="cart.html" title="Cart"> <area shape="rect" coords="106,0,136,31" href="settings.html" title="Settings"> <area shape="rect" coords="141,0,171,31" href="help.html" title="Help"> </map>
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - CSS Sprites
- InhaltsvorschauLike image maps, CSS sprites allow you to combine images, but they're much more flexible. The concept reminds me of a Ouija board, where the planchette (the viewer that all participants hold on to) moves around the board stopping over different letters. To use CSS sprites, multiple images are combined into a single image, similar to the one shown in . This is the "Ouija board."
Figure : CSS sprites combine multiple images into a single imageThe "planchette" is any HTML element that supports background images, such as aSPANorDIV. The HTML element is positioned over the desired part of the background image using the CSSbackground-positionproperty. For example, you can use the "My" icon for an element's background image as follows:<div style="background-image: url('a_lot_of_sprites.gif'); background-position: −260px −90px; width: 26px; height: 24px;"> </div>I modified the previous image map example to use CSS sprites. The five links are contained in aDIVnamednavbar. Each link is wrapped around aSPANthat uses a single background image,spritebg.gif, as defined in the#navbar spanrule. EachSPANhas a different class that specifies the offset into the CSS sprite using thebackground-positionproperty:<style> #navbar span { width:31px; height:31px; display:inline; float:left; background-image:url(/images/spritebg.gif); } .home { background-position:0 0; margin-right:4px; margin-left: 4px;} .gifts { background-position:-32px 0; margin-right:4px;} .cart { background-position:-64px 0; margin-right:4px;} .settings { background-position:-96px 0; margin-right:4px;} .help { background-position:-128px 0; margin-right:0px;} </style> <div id="navbar" style="background-color: #F4F5EB; border: 2px ridge #333; width: 180px; height: 32px; padding: 4px 0 4px 0;"> <a href="javascript:alert('Home')"><span class="home"></span></a> <a href="javascript:alert('Gifts')"><span class="gifts"></span></a> <a href="javascript:alert('Cart')"><span class="cart"></span></a> <a href="javascript:alert('Settings')"><span class="settings"></span></a> <a href="javascript:alert('Help')"><span class="help"></span></a> </div>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Inline Images
- InhaltsvorschauIt's possible to include images in your web page without any additional HTTP requests by using the
data:URL scheme. Although this approach is not currently supported in Internet Explorer, the savings it can bring to other browsers makes it worth mentioning.We're all familiar with URLs that include thehttp:scheme. Other schemes include the familiarftp:,file:, andmailto:schemes. But there are many more schemes, such assmtp:,pop:,dns:,whois:,finger:,daytime:,news:, andurn:. Some of these are officially registered; others are accepted because of their common usage.Thedata:URL scheme was first proposed in 1995. The specification (http://tools.ietf.org/html/rfc2397) says it "allows inclusion of small data items as 'immediate' data." The data is in the URL itself following this format:data:[<mediatype>][;base64],<data>
An inline image of a red star is specified as:<IMG ALT="Red Star" SRC="data:image/gif;base64,R0lGODlhDAAMALMLAPN8ffBiYvWW lvrKy/FvcPewsO9VVfajo+w6O/zl5estLv/8/AAAAAAAAAAAAAAAACH5BAEA AAsALAAAAAAMAAwAAAQzcElZyryTEHyTUgknHd9xGV+qKsYirKkwDYiKDBia tt2H1KBLQRFIJAIKywRgmhwAIlEEADs=">
I've seendata:used only for inline images, but it can be used anywhere a URL is specified, includingSCRIPTandAtags.The main drawback of thedata:URL scheme is that it's not supported in Internet Explorer (up to and including version 7). Another drawback is its possible size limitations, but Firefox 1.5 accepts inline images up to 100K. The base64 encoding increases the size of images, so the total size downloaded is increased.The navbar from previous sections is implemented using inline images in the following example.- Inline Images
Becausedata:URLs are embedded in the page, they won't be cached across different pages. You might not want to inline your company logo, because it would make every page grow by the encoded size of the logo. A clever way around this is to use CSS and inline the image as a background. Placing this CSS rule in an external stylesheet means that the data is cached inside the stylesheet. In the following example, the background images used for each link in the navbar are implemented using inline images in an external stylesheet.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Combined Scripts and Stylesheets
- InhaltsvorschauJavaScript and CSS are used on most web sites today. Frontend engineers must choose whether to "inline" their JavaScript and CSS (i.e., embed it in the HTML document) or include it from external script and stylesheet files. In general, using external scripts and stylesheets is better for performance (this is discussed more in ). However, if you follow the approach recommended by software engineers and modularize your code by breaking it into many small files, you decrease performance because each file results in an additional HTTP request.shows that 10 top web sites average six to seven scripts and one to two stylesheets on their home pages. These web sites were selected from http://www.alexa.com, as described in . Each of these sites requires an additional HTTP request if it's not cached in the user's browser. Similar to the benefits of image maps and CSS sprites, combining these separate files into one file reduces the number of HTTP requests and improves the end user response time.
Table : Number of scripts and stylesheets for 10 top sites Web siteScriptsStylesheets3118111272119122314173To be clear, I'm not suggesting combining scripts with stylesheets. Multiple scripts should be combined into a single script, and multiple stylesheets should be combined into a single stylesheet. In the ideal situation, there would be no more than one script and one stylesheet in each page.The following examples show how combining scripts improves the end user response time. The page with the combined scripts loads 38% faster. Combining stylesheets produces similar performance improvements. For the rest of this section I'll talk only about scripts (because they're used in greater numbers), but everything discussed applies equally to stylesheets.- Separate Scripts
- Combined Scripts
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauThis chapter covered the techniques we've used at Yahoo! to reduce the number of HTTP requests in web pages without compromising the pages' design. The rules described in later chapters also present guidelines that help reduce the number of HTTP requests, but they focus primarily on subsequent page views. For components that are not critical to the initial rendering of the page, the post-onload download technique described in helps by postponing these HTTP requests until after the page is loaded.This chapter's rule is the one that is most effective in reducing HTTP requests for first-time visitors to your web site; that's why I put it first, and why it's the most important rule. Following its guidelines improves both first-time views and subsequent views. A fast response time on that first page view can make the difference between a user who abandons your site and one who comes back again and again.Make fewer HTTP requests.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 4: Rule 2: Use a Content Delivery Network
- InhaltsvorschauThe average user's bandwidth increases every year, but a user's proximity to your web server still has an impact on a page's response time. Web startups often have all their servers in one location. If they survive the startup phase and build a larger audience, these companies face the reality that a single server location is no longer sufficient—it's necessary to deploy content across multiple, geographically dispersed servers.As a first step to implementing geographically dispersed content, don't attempt to redesign your web application to work in a distributed architecture. Depending on the application, a redesign could include daunting tasks such as synchronizing session state and replicating database transactions across server locations. Attempts to reduce the distance between users and your content could be delayed by, or never pass, this redesign step.The correct first step is found by recalling the Performance Golden Rule, described in :Only 10–20% of the end user response time is spent downloading the HTML document. The other 80–90% is spent downloading all the components in the page.If the application web servers are closer to the user, the response time of one HTTP request is improved. On the other hand, if the component web servers are closer to the user, the response times of many HTTP requests are improved. Rather than starting with the difficult task of redesigning your application in order to disperse the application web servers, it's better to first disperse the component web servers. This not only achieves a bigger reduction in response times, it's also easier thanks to content delivery networks.A content delivery network (CDN) is a collection of web servers distributed across multiple locations to deliver content to users more efficiently. This efficiency is typically discussed as a performance issue, but it can also result in cost savings. When optimizing for performance, the server selected for delivering content to a specific user is based on a measure of network proximity. For example, the CDN may choose the server with the fewest network hops or the server with the quickest response time.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Content Delivery Networks
- InhaltsvorschauA content delivery network (CDN) is a collection of web servers distributed across multiple locations to deliver content to users more efficiently. This efficiency is typically discussed as a performance issue, but it can also result in cost savings. When optimizing for performance, the server selected for delivering content to a specific user is based on a measure of network proximity. For example, the CDN may choose the server with the fewest network hops or the server with the quickest response time.Some large Internet companies own their own CDN, but it's cost effective to use a CDN service provider. Akamai Technologies, Inc. is the industry leader. In 2005, Akamai acquired Speedera Networks, the primary low-cost alternative. Mirror Image Internet, Inc. is now the leading alternative to Akamai. Limelight Networks, Inc. is another competitor. Other providers, such as SAVVIS Inc., specialize in niche markets such as video content delivery.shows 10 top Internet sites in the U.S. and the CDN service providers they use.
Table : CDN service providers used by top sites Web siteCDNAkamaiAkamaiAkamai, Mirror ImageSAVVISAkamai, LimelightAkamaiYou can see that:- Five use Akamai
- One uses Mirror Image
- One uses Limelight
- One uses SAVVIS
- Four either don't use a CDN or use a homegrown CDN solution
Smaller and noncommercial web sites might not be able to afford the cost of these CDN services. There are several free CDN services available. Globule (http://www.globule.org) is an Apache module developed at Vrije Universiteit in Amsterdam. CoDeeN (http://codeen.cs.princeton.edu) was built at Princeton University on top of PlanetLab. CoralCDN (http://www.coralcdn.org) is run out of New York University. They are deployed in different ways. Some require that end users configure their browsers to use a proxy. Others require developers to change the URL of their components to use a different hostname. Be wary of any that use HTTP redirects to point users to a local server, as this slows down web pages (see ).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Savings
- InhaltsvorschauThe two online examples discussed in this section demonstrate the response time improvements gained from using a CDN. Both examples include the same test components: five scripts, one stylesheet, and eight images. In the first example, these components are hosted on the Akamai Technologies CDN. In the second example, they are hosted on a single web server.The example with components hosted on the CDN loaded 18% faster than the page with all components hosted from a single web server (1013 milliseconds versus 1232 milliseconds). I tested this over DSL (~900 Kbps) from my home in California. Your results will vary depending on your connection speed and geographic location. The single web server is located near Washington, DC. The closer you live to Washington, DC, the less of a difference you'll see in response times in the CDN example.If you conduct your own response time tests to gauge the benefits of using a CDN, it's important to keep in mind that the location from which you run your test has an impact on the results. For example, based on the assumption that most web companies choose a data center close to their offices, your web client at work is probably located close to your current web servers. Thus, if you run a test from your browser at work, the response times without using a CDN are often best case. It's important to remember that most of your users are not located that close to your web servers. To measure the true impact of switching to a CDN, you need to measure the response times from multiple geographic locations. Services such as Keynote Systems (http://www.keynote.com) and Gomez (http://www.gomez.com) are helpful for conducting such tests.At Yahoo!, this factor threw us off for awhile. Before switching Yahoo! Shopping to Akamai, our preliminary tests were run from a lab at Yahoo! headquarters, located near a Yahoo! data center. The response time improvements gained by switching to Akamai's CDN—as measured from that lab—were less than 5% (not very impressive). We knew the response time improvements would be better when we exposed the CDN change to our live users, spread around the world. When we exposed the change to end users, there was an overall 20% reduction in response times on the Yahoo! Shopping site, just from moving all the static components to a CDN.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 5: Rule 3: Add an Expires Header
- InhaltsvorschauFast response time is not your only consideration when designing web pages. If it were, then we'd all take Rule 1 to an extreme and place no images, scripts, or stylesheets in our pages. However, we all understand that images, scripts, and stylesheets can enhance the user experience, even if it means that the page will take longer to load. Rule 3, described in this chapter, shows how you can improve page performance by making sure these components are configured to maximize the browser's caching capabilities.Today's web pages include many components and that number continues to grow. A first-time visitor to your page may have to make several HTTP requests, but by using a future
Expiresheader, you make those components cacheable. This avoids unnecessary HTTP requests on subsequent page views. A futureExpiresheader is most often used with images, but it should be used on all components, including scripts, stylesheets, and Flash. Most top web sites are not currently doing this. In this chapter, I point out these sites and show why their pages aren't as fast as they could be. Adding a futureExpiresheader incurs some additional development costs, as described in the section "."Browsers (and proxies) use a cache to reduce the number of HTTP requests and decrease the size of HTTP responses, thus making web pages load faster. A web server uses theExpiresheader to tell the web client that it can use the current copy of a component until the specified time. The HTTP specification summarizes this header as "the date/time after which the response is considered stale." It is sent in the HTTP response.Expires: Thu, 15 Apr 2010 20:00:00 GMT
This is a far future Expires header, telling the browser that this response won't be stale until April 15, 2010. If this header is returned for an image in a page, the browser uses the cached image on subsequent page views, reducing the number of HTTP requests by one. See for a review of theExpiresheader and HTTP.Before I explain how better caching improves performance, it's important to mention an alternative to theExpiresEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Expires Header
- InhaltsvorschauBrowsers (and proxies) use a cache to reduce the number of HTTP requests and decrease the size of HTTP responses, thus making web pages load faster. A web server uses the
Expiresheader to tell the web client that it can use the current copy of a component until the specified time. The HTTP specification summarizes this header as "the date/time after which the response is considered stale." It is sent in the HTTP response.Expires: Thu, 15 Apr 2010 20:00:00 GMT
This is a far future Expires header, telling the browser that this response won't be stale until April 15, 2010. If this header is returned for an image in a page, the browser uses the cached image on subsequent page views, reducing the number of HTTP requests by one. See for a review of theExpiresheader and HTTP.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Max-Age and mod_expires
- InhaltsvorschauBefore I explain how better caching improves performance, it's important to mention an alternative to the
Expiresheader. TheCache-Controlheader was introduced in HTTP/1.1 to overcome limitations with theExpiresheader. Because theExpiresheader uses a specific date, it has stricter clock synchronization requirements between server and client. Also, the expiration dates have to be constantly checked, and when that future date finally arrives, a new date must be provided in the server's configuration.Alternatively,Cache-Controluses themax-agedirective to specify how long a component is cached. It defines the freshness window in seconds. If less thanmax-ageseconds have passed since the component was requested, the browser will use the cached version, thus avoiding an additional HTTP request. A far futuremax-ageheader might set the freshness window 10 years in the future.Cache-Control: max-age=315360000
UsingCache-Controlwithmax-ageovercomes the limitations ofExpires, but you still might want anExpiresheader for browsers that don't support HTTP/1.1 (even though this is probably less than 1% of your traffic). You could specify both response headers,ExpiresandCache-Control max-age. If both are present, the HTTP specification dictates that themax-agedirective will override theExpiresheader. However, if you're conscientious, you'll still worry about the clock synchronization and configuration maintenance issues withExpires.Fortunately, themod_expiresApache module (http://httpd.apache.org/docs/2.0/mod/mod_expires.html) lets you use anExpiresheader that sets the date in a relative fashion similar tomax-age. This is done via theExpiresDefaultdirective. In this example, the expiration date for images, scripts, and stylesheets is set 10 years from the time of the request:<FilesMatch "\.(gif|jpg|js|css)$"> ExpiresDefault "access plus 10 years" </FilesMatch>
The time can be specified in years, months, weeks, days, hours, minutes, or seconds. It sends both anExpiresheader and aCache-Control max-ageheader in the response.Expires: Sun, 16 Oct 2016 05:43:02 GMT Cache-Control: max-age=315360000
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Empty Cache vs. Primed Cache
- InhaltsvorschauUsing a far future
Expiresheader affects page views only after a user has already visited your site. It has no effect on the number of HTTP requests when a user visits your site for the first time and the browser's cache is empty. Therefore, the impact of this performance improvement depends on how often users hit your pages with a primed cache. It's likely that a majority of your traffic comes from users with a primed cache. Making your components cacheable improves the response time for these users.When I say "empty cache" or "primed cache," I mean the state of the browser's cache relative to your page. The cache is "empty" if none of your page's components are in the cache. The browser's cache might contain components from other web sites, but that doesn't help your page. Conversely, the cache is "primed" if all of your page's cacheable components are in the cache.The number of empty versus primed cache page views depends on the nature of the web application. A site like "word of the day" might only get one page view per session from the typical user. There are several reasons why the "word of the day" components might not be in the cache the next time a user visits the site:- Despite her desire for a better vocabulary, a user may visit the page only weekly or monthly, rather than daily.
- A user may have manually cleared her cache since her last visit.
- A user may have visited so many other web sites that her cache filled up, and the "word of the day" components were pushed out.
- The browser or an antivirus application may have cleared the cache when the browser was closed.
With only one page view per session, it's not very likely that "word of the day" components are in the cache, so the percentage of primed cache page views is low.On the other hand, a travel or email web site might get multiple page views per user session and the number of primed cache page views is likely to be high. In this instance, more page views will find your components in the browser's cache.We measured this at Yahoo! and found that the number of unique users who came in at least once a day with a primed cache ranged from 40–60%Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - More Than Just Images
- InhaltsvorschauUsing a far future
Expiresheader on images is fairly common, but this best practice should not be limited to images. A far futureExpiresheader should be included on any component that changes infrequently, including scripts, stylesheets, and Flash components. Typically, an HTML document won't have a futureExpiresheader because it contains dynamic content that is updated on each user request.In the ideal situation, all the components in a page would have a far futureExpiresheader, and subsequent page views would make just a single HTTP request for the HTML document. When all of the document's components are read from the browser's cache, the response time is cut by 50% or more.I surveyed 10 top Internet sites in the U.S and recorded how many of the images, scripts, and stylesheets had anExpiresor aCache-Control max-ageheader set at least 30 days in the future. As shown in , the news isn't good. Three types of components are tallied: images, stylesheets, and scripts. shows the number of components that are cacheable for at least 30 days out of the total number of components of each type. Let's see to what extent these sites employ the practice of making their components cacheable:- Five sites make a majority of their images cacheable for 30 days or more.
- Four sites make a majority of their stylesheets cacheable for 30 days or more.
- Two sites make a majority of their scripts cacheable for 30 days or more.
Table : Components with an Expires header Web siteImagesStylesheetsScriptsMedian Last-Modified Δ0/620/10/3114 days23/431/16/18217 days0/1380/22/11227 days16/200/20/7140 days1/230/10/1454 days32/351/13/934 days0/180/20/21 day6/81/12/31 day23/231/14/4-0/320/30/726 daysThe overall percentage from indicates that 74.7% of all components were either not cacheable or were cacheable for less than 30 days. One possible explanation is that these components shouldn't be cached. For example, a news site such asEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Revving Filenames
- InhaltsvorschauIf we configure components to be cached by browsers and proxies, how do users get updates when those components change? When an
Expiresheader is present, the cached version is used until the expiration date. The browser doesn't check for any changes until after the expiration date has passed. That's why using theExpiresheader significantly reduces response times—browsers read the components straight from disk without generating any HTTP traffic. Thus, even if you update the component on your servers, users who have already been to your site will most likely not get the updated component (since the previous version is in their cache).To ensure users get the latest version of a component, change the component's filename in all of your HTML pages. Mark Nottingham's web article "Caching Tutorial for Web Authors and Webmasters" says:The most effective solution is to change any links to them; that way, completely new representations will be loaded fresh from the origin server.Depending on how you construct your HTML pages, this practice may be either trivial or painful. If you generate your HTML pages dynamically using PHP, Perl, etc., a simple solution is to use variables for all your component filenames. With this approach, updating a filename across all your pages is as simple as changing the variable in one location. At Yahoo! we often make this step part of the build process: a version number is embedded in the component's filename (for example, yahoo_2.0.6.js) and the revved filename is automatically updated in the global mapping. Embedding the version number not only changes the filename, it also makes it easier to find the exact source code files when debugging.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Examples
- InhaltsvorschauThe following two examples demonstrate the performance improvement achieved by using a far future
Expiresheader. Both examples include the same components: six images, three scripts, and one stylesheet. In the first example, these components do not have a far futureExpiresheader. In the second example, they do.- No Expires
- Far Future Expires
Adding the far futureExpiresheader drops the response time for subsequent page views from ~600 milliseconds to ~260 milliseconds, a 57% reduction when tested over DSL at 900 Kbps. With more components in the page, response times improve even more. If your pages average more than six images, three scripts, and one stylesheet, your pages should show a speed up greater than the 57% I found in my example.Where exactly do these response time savings come from? As I mentioned earlier, a component with a far futureExpiresheader is cached, and on subsequent requests the browser reads it straight from disk, avoiding an HTTP request. However, I didn't describe the converse. If a component does not have a far futureExpiresheader, it's still stored in the browser's cache. On subsequent requests the browser checks the cache and finds that the component is expired (in HTTP terms it is "stale"). For efficiency, the browser sends a conditional GET request to the origin server. See for an example. If the component hasn't changed, the origin server avoids sending back the entire component and instead sends back a few headers telling the browser to use the component in its cache.Those conditional requests add up. That's where the savings come from. Most of the time, as we saw when looking at the 10 top web sites, the component hasn't changed and the browser is going to read it from disk anyway. You can cut your response times in half by using theExpiresheader to avoid these unnecessary HTTP requests.Add a far futureExpiresheader to your components.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 6: Rule 4: Gzip Components
- InhaltsvorschauThe time it takes to transfer an HTTP request and response across the network can be significantly reduced by decisions made by frontend engineers. It's true that the end user's bandwidth speed, Internet service provider, proximity to peering exchange points, and other factors are beyond the control of the development team. However, there are more variables that affect response times. Rules 1 and 3 address response times by eliminating unnecessary HTTP requests. If there is no HTTP request then there is no network activity—the ideal situation. Rule 2 improves response times by bringing the HTTP response closer to the user.Rule 4, examined in this chapter, reduces response times by reducing the size of the HTTP response. If an HTTP request results in a smaller response, the transfer time decreases because fewer packets must travel from the server to the client. This effect is even greater for slower bandwidth speeds. This chapter shows how to use gzip encoding to compress HTTP responses, and thus reduce network response times. This is the easiest technique for reducing page weight and it also has the biggest impact. There are other ways you can reduce the HTML document's page weight (strip comments and shorten URLs, for example), but they are typically less effective and require more work.The same file compression that has been used for decades to reduce file sizes in email messages and on FTP sites is also used to deliver compressed web pages to browsers. Starting with HTTP/1.1, web clients indicate support for compression with the
Accept-Encodingheader in the HTTP request.Accept-Encoding: gzip, deflate
If the web server sees this header in the request, it may compress the response using one of the methods listed by the client. The web server notifies the web client of this via theContent-Encodingheader in the response.Content-Encoding: gzip
Gzip is currently the most popular and effective compression method. It is a free format (i.e., unencumbered by patents or other restrictions) developed by the GNU project and standardized by RFC 1952. The only other compression format you're likely to see isEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - How Compression Works
- InhaltsvorschauThe same file compression that has been used for decades to reduce file sizes in email messages and on FTP sites is also used to deliver compressed web pages to browsers. Starting with HTTP/1.1, web clients indicate support for compression with the
Accept-Encodingheader in the HTTP request.Accept-Encoding: gzip, deflate
If the web server sees this header in the request, it may compress the response using one of the methods listed by the client. The web server notifies the web client of this via theContent-Encodingheader in the response.Content-Encoding: gzip
Gzip is currently the most popular and effective compression method. It is a free format (i.e., unencumbered by patents or other restrictions) developed by the GNU project and standardized by RFC 1952. The only other compression format you're likely to see is deflate, but it's slightly less effective and much less popular. In fact, I have seen only one site that uses deflate: msn.com. Browsers that support deflate also support gzip, but several browsers that support gzip do not support deflate, so gzip is the preferred method of compression.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - What to Compress
- InhaltsvorschauServers choose what to gzip based on file type, but are typically too limited in what they are configured to compress. Many web sites gzip their HTML documents. It's also worthwhile to gzip your scripts and stylesheets, but many web sites miss this opportunity (in fact, it's worthwhile to compress any text response including XML and JSON, but the focus here is on scripts and stylesheets since they're the most prevalent). Image and PDF files should not be gzipped because they are already compressed. Trying to gzip them not only wastes CPU resources, it can also potentially increase file sizes.There is a cost to gzipping: it takes additional CPU cycles on the server to carry out the compression and on the client to decompress the gzipped file. To determine whether the benefits outweigh the costs you would have to consider the size of the response, the bandwidth of the connection, and the Internet distance between the client and the server. This information isn't generally available, and even if it were, there would be too many variables to take into consideration. Generally, it's worth gzipping any file greater than 1 or 2K. The
mod_gzip_minimum_file_sizedirective controls the minimum file size you're willing to compress. The default value is 500 bytes.I looked at the use of gzip on 10 of the most popular U.S. web sites. Nine sites gzipped their HTML documents, seven sites gzipped most of their scripts and stylesheets, and only five gzipped all of their scripts and stylesheets. The sites that don't compress all of their HTML, stylesheets, and scripts are missing the opportunity to reduce the page weight by up to 70%, as we'll see in the next section, "." A survey of major web sites and what they choose to compress is shown in .Table : Gzip use for 10 popular U.S. web sites Web siteGzip HTMLGzip scriptsGzip stylesheets✓✓somesome✓✓✓✓✓deflatedeflate✓✓✓✓✓✓Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Savings
- InhaltsvorschauGzipping generally reduces the response size by about 70%. shows examples of size reductions for scripts and stylesheets (small and large). In addition to gzip, the results for deflate are also shown.
Table : Compression sizes using gzip and deflate File typeUncompressed sizeGzip sizeGzip savingsDeflate sizeDeflate savingsScript3,277 bytes1076 bytes67%1112 bytes66%Script39,713 bytes14,488 bytes64%16,583 bytes58%Stylesheet968 bytes426 bytes56%463 bytes52%Stylesheet14,122 bytes3,748 bytes73%4,665 bytes67%It's clear from why gzip is typically the choice for compression. Gzip reduces the response by about 66% overall, while deflate reduces the response by 60%. For these files, gzip compresses ~6% more than deflate.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Configuration
- InhaltsvorschauThe module used for configuring gzip depends on your version of Apache: Apache 1.3 uses
mod_gzipwhile Apache 2.x usesmod_deflate. This section describes how to configure each module, and focuses on Apache because it is the most popular web server on the Internet.Gzip compression for Apache 1.3 is provided by themod_gzipmodule. There are manymod_gzipconfiguration directives, and these are described on themod_gzipweb site (http://www.schroepl.net/projekte/mod_gzip). Here are the most commonly used directives:mod_gzip_on- Enables mod_gzip.
mod_gzip_item_includemod_gzip_item_exclude- Define which requests to gzip or not to gzip based on file type, MIME type, user agent, etc.
Most web hosting services havemod_gzipturned on fortext/htmlby default. The most important configuration change you should make is to explicitly compress scripts and stylesheets. You can do this using the following Apache 1.3 directives:mod_gzip_item_include file \.js$ mod_gzip_item_include mime ^application/x-javascript$ mod_gzip_item_include file \.css$ mod_gzip_item_include mime ^text/css$
The gzip command-line utility offers an option that controls the degree of compression, trading off CPU usage for size reduction, but there is no configuration directive to control the compression level inmod_gzip. If the CPU load caused by streaming compression is an issue, consider caching the compressed responses, either on disk or in memory. Compressing your responses and updating the cache manually adds to your maintenance work and can become a burden. Fortunately, there are options formod_gzipto automatically save the gzipped content to disk and update that gzipped content when the source changes. Use themod_gzip_can_negotiateandmod_gzip_update_staticdirectives to do this.Compression in Apache 2.x is done with themod_deflatemodule. Despite the name of the module, it does compression using gzip. The basic configuration shown in the previous section for compressing scripts and stylesheets is done in one line:AddOutputFilterByType DEFLATE text/html text/css application/x-javascript
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Proxy Caching
- InhaltsvorschauThe configuration described so far works fine when the browser talks directly to the web server. The web server determines whether to compress the response based on
Accept-Encoding. The browser caches the response, whether or not it has been compressed, based on other HTTP headers in the response such asExpiresandCache-Control(see ).When the browser sends the request through a proxy it gets more complicated. Suppose that the first request to the proxy for a certain URL comes from a browser that does not support gzip. This is the first request to the proxy, so its cache is empty. The proxy forwards that request to the web server. The web server's response is uncompressed. That uncompressed response is cached by the proxy and sent on to the browser. Now, suppose the second request to the proxy for the same URL comes from a browser that does support gzip. The proxy responds with the (uncompressed) contents in its cache, missing the opportunity to use gzip. The situation is worse if the sequence is reversed: when the first request is from a browser that supports gzip and the second request is from a browser that doesn't. In this case, the proxy has a compressed version of the contents in its cache and serves that version to all subsequent browsers whether they support gzip or not.The way around this problem is to add theVaryheader in the response from your web server. The web server tells the proxy to vary the cached responses based on one or more request headers. Because the decision to compress is based on theAccept-Encodingrequest header, it makes sense to includeAccept-Encodingin the server'sVaryresponse header.Vary: Accept-Encoding
This causes the proxy to cache multiple versions of the response, one for each value of theAccept-Encodingrequest header. In our previous example, the proxy would cache two versions of each response: the compressed content for whenAccept-Encodingisgzipand the uncompressed content for whenAccept-Encodingis not specified at all. When a browser hits the proxy with the request headerAccept-Encoding: gzipit receives the compressed response. Browsers without anEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Edge Cases
- InhaltsvorschauThe coordination of compression between servers and clients seems simple, but it must work correctly. The page could easily break if either the client or server makes a mistake (sending gzipped content to a client that can't understand it, forgetting to declare a compressed response as gzip-encoded, etc.). Mistakes don't happen often, but there are edge cases to take into consideration.Approximately 90% of today's Internet traffic travels through browsers that claim to support gzip. If a browser says it supports gzip you can generally trust it. There are some known bugs with unpatched early versions of Internet Explorer, specifically Internet Explorer 5.5 and Internet Explorer 6.0 SP1, and Microsoft has published two Knowledge Base articles documenting the problem (http://support.microsoft.com/kb/313712/en-us and http://support.microsoft.com/kb/312496/en-us). There are other known problems, but they occur on browsers that represent less than 1% of Internet traffic. A safe approach is to serve compressed content only for browsers that are proven to support it, such as Internet Explorer 6.0 and later and Mozilla 5.0 and later. This is called a browser whitelist approach.With this approach you may miss the opportunity to serve compressed content to a few browsers that would have supported it. The alternative—serving compressed content to a browser that can't support it—is far worse. Using
mod_gzipin Apache 1.3, a browser whitelist is specified usingmod_gzip_item_includewith the appropriateUser-Agentvalues:mod_gzip_item_include reqheader "User-Agent: MSIE [6-9]" mod_gzip_item_include reqheader "User-Agent: Mozilla/[5-9]"
In Apache 2.x use theBrowserMatchdirective:BrowserMatch ^MSIE [6-9] gzip BrowserMatch ^Mozilla/[5-9] gzip
Adding proxy caches to the mix complicates the handling of these edge case browsers. It's not possible to share your browser whitelist configuration with the proxy. The directives used to set up the browser whitelist are too complex to encode using HTTP headers. The best you can do is addUser-Agentto theVaryheader as another criterion for the proxy.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Gzip in Action
- InhaltsvorschauThree examples for Rule 4 demonstrate the different degrees of compression you can deploy across your site.
- Nothing Gzipped
- HTML Gzipped
- Everything Gzipped
In addition to the 48.6K HTML document, each example page contains a 59.9K stylesheet and a 68.0K script. shows how the total page size varies with the amount of compression that is performed. Compressing the HTML document, stylesheet, and script reduces this page size from 177.6K to 46.4K, a size reduction of 73.8%! Compression typically reduces the content size by approximately 70%, but it varies depending on the amount of whitespace and character repetition.Table : Page weight savings for different levels of compression ExampleComponents (HTML, CSS, JS)Total sizeSize savingsResponse timeTime savingsNothing Gzipped48.6K, 59.9K, 68.0K177.6K-1562 ms-HTML Gzipped13.9K, 59.9K, 68.0K141.9K34.7K (19.7%)1411 ms151 ms (9.7%)Everything Gzipped13.9K, 14.4K, 18.0K46.4K130.2K (73.8%)731 ms831 ms (53.2%)The page with everything compressed loads ~831 milliseconds faster than the noncompressed example, a response time reduction of 53.2%. This was measured over a 900 Kbps DSL line. The absolute response time values vary depending on Internet connection, CPU, browser, geographic location, etc. However, the relative savings remain about the same. A simple change in your web server configuration, compressing as many components as possible, dramatically improves the speed of your web pages.Gzip your scripts and stylesheets.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 7: Rule 5: Put Stylesheets at the Top
- InhaltsvorschauA team running a major portal at Yahoo! added several DHTML features to their page while trying to ensure there was no adverse effect on response times. One of the more complex DHTML features, a pop-up
DIVfor sending email messages, was not part of the actual rendering of the page—it was accessible only after the page had loaded and the user clicked on the button to send the email message. Since it wasn't used to render the page, the frontend engineer put the CSS for the pop-upDIVin an external stylesheet and added the correspondingLINKtag at the bottom of the page with the expectation that including it at the end would make the page load faster.The logic behind this made sense. Many other components (images, stylesheets, scripts, etc.) were required to render the page. Since components are (in general) downloaded in the order in which they appear in the document, putting the DHTML feature's stylesheet last would allow the more critical components to be downloaded first, resulting in a faster-loading page.Or would it?In Internet Explorer (still the most popular browser) the resulting page was noticeably slower than the old design. While trying to find ways to speed up the page, we discovered that moving the DHTML feature's stylesheet to the top of the document, in theHEAD, made the page load faster. This contradicted what we expected. How could putting the stylesheet first, thus delaying the critical components in the page, actually improve the page load time? Further investigation led to the creation of Rule 5.Frontend engineers who care about performance want a page to load progressively; that is, we want the browser to display whatever content it has as soon as possible. This is especially important for pages with a lot of content and for users on slower Internet connections. The importance of giving users visual feedback has been well researched and documented. In his web article, Jakob Nielson, pioneering usability engineer, stresses the importance of visual feedback in terms of a progress indicator.Progress indicators have three main advantages: They reassure the user that the system has not crashed but is working on his or her problem; they indicate approximately how long the user can be expected to wait, thus allowing the user to do other activities during long waits; and they finally provide something for the user to look at, thus making the wait less painful. This latter advantage should not be underestimated and is one reason for recommending a graphic progress bar instead of just stating the expected remaining time in numbers.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Progressive Rendering
- InhaltsvorschauFrontend engineers who care about performance want a page to load progressively; that is, we want the browser to display whatever content it has as soon as possible. This is especially important for pages with a lot of content and for users on slower Internet connections. The importance of giving users visual feedback has been well researched and documented. In his web article, Jakob Nielson, pioneering usability engineer, stresses the importance of visual feedback in terms of a progress indicator.Progress indicators have three main advantages: They reassure the user that the system has not crashed but is working on his or her problem; they indicate approximately how long the user can be expected to wait, thus allowing the user to do other activities during long waits; and they finally provide something for the user to look at, thus making the wait less painful. This latter advantage should not be underestimated and is one reason for recommending a graphic progress bar instead of just stating the expected remaining time in numbers.In our case the HTML page is the progress indicator. When the browser loads the page progressively, the header, the navigation bar, the logo at the top, etc. all serve as visual feedback for the user who is waiting for the page. This improves the overall user experience.The problem with putting stylesheets near the bottom of the document is that it prohibits progressive rendering in many browsers. Browsers block rendering to avoid having to redraw elements of the page if their styles change. Rule 5 has less to do with the actual time to load the page's components and more to do with how the browser reacts to the order of those components. In fact, the page that feels slower is ironically the page that loads the visible components faster. The browser delays showing any visible components while it and the user wait for the stylesheet at the bottom. The examples in the following section demonstrate this phenomenon, which I call the "."Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- sleep.cgi
- InhaltsvorschauWhile building the examples of this phenomenon, I developed a tool that I've found extremely useful for showing how delayed components affect web pages: sleep.cgi. It's a simple Perl CGI program that takes the following parameters:
sleep- How long (in seconds) the response should be delayed. The default is
0. type- The type of component to return. Possible values are
gif,js,css,html, andswf. The default value isgif. expires- One of three values:
−1(returns anExpiresheader in the past),0(noExpiresheader is returned), and1(returns anExpiresheader in the future). The default is1. last- A value of
−1returns aLast-Modifiedheader with a date equal to the file's timestamp. A value of0results in noLast-Modifiedheader being returned. The default is−1. redir- A value of
1causes a 302 response that redirects back to the exact same URL withredir=1removed.
The first example requires some slow images and a slow stylesheet. Those are achieved with the following requests to sleep.cgi:<img src="/bin/sleep.cgi?type=gif&sleep=2&expires=-1&last=0"> <link rel="stylesheet" href="/bin/sleep.cgi?type=css&sleep=1&expires=-1&last=0">
Both the image and stylesheet use theexpires=−1option to get a response that has anExpiresheader in the past. This prevents the components from being cached so that you can run the test repeatedly and get the same experience each time (I also add a unique timestamp to each component's URL to further prevent caching). In order to reduce the variables in this test, I specifylast=0to remove theLast-Modifiedheader from the response. The image request has a two-second delay (sleep=2), while the stylesheet is delayed only one second (sleep=1). This ensures that any delay seen is not due to the stylesheet's response time, but instead to its blocking behavior (which is what the page is testing).Being able to exaggerate the response times of components makes it possible to visualize their effects on page loading and response times. I've made the Perl code available so others can use it for their own testing (http://stevesouders.com/hpws/sleep.txtEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Blank White Screen
- InhaltsvorschauThis section shows two web pages that differ in just one respect: whether the stylesheet is at the top or bottom of the page. What a difference it makes to the user experience!The first example demonstrates the harm of putting stylesheets at the bottom of the HTML document.
- CSS at the Bottom
Notice how putting stylesheets near the end of the document can delay page loading. This problem is harder to track down because it only happens in Internet Explorer and depends on how the page is loaded. After working with the page, you'll notice that it occasionally loads slowly. When this happens, the page is completely blank until all the content blasts onto the screen at once, as illustrated in . Progressive rendering has been thwarted. This is a bad user experience because there is no visual feedback to reassure the user that her request is being handled correctly. Instead, the user is left to wonder whether anything is happening. That's the moment when a user abandons your web site and navigates to your competitor.
Figure : The blank white screenHere are the cases where putting stylesheets at the bottom of the document causes the blank white screen problem to surface in Internet Explorer:- In a new window
- Clicking the "new window" link in the example page opens "CSS at the Bottom" in a new window. Users often open new windows when navigating across sites, such as when going from a search results page to the actual target page.
- As a reload
- Clicking the Refresh button, a normal user behavior, is another way to trigger a blank white screen. Minimize and restore the window while the page is loading to see the blank white screen.
- As a home page
- Setting the browser's default page to http://stevesouders.com/hpws/css-bottom.php and opening a new browser window causes the blank white screen. Rule 5 is important for any team that wants its web site to be used as a home page.
To avoid the blank white screen, move the stylesheet to the top in the document'sHEAD. Doing this in the sample web site I've called "CSS at the Top" solves all the problem scenarios. No matter how the page is loaded—whether in a new window, as a reload, or as a home page—the page renders progressively.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Flash of Unstyled Content
- InhaltsvorschauThe blank white screen phenomenon is due to browser behavior. Remember that our stylesheet wasn't even used to render the page—only to affect the DHTML feature for sending email messages. Even when Internet Explorer had all the necessary components, it waited to render them until the unnecessary stylesheet was also downloaded. The location of the stylesheet in the page doesn't affect download times, but it does affect rendering. David Hyatt has a great explanation of why the browser does this.If stylesheets are still loading, it is wasteful to construct the rendering tree, since you don't want to paint anything at all until all stylesheets have been loaded and parsed. Otherwise you'll run into a problem called FOUC (the flash of unstyled content problem), where you show content before it's ready.The following example demonstrates this problem.
- CSS Flash of Unstyled Content
In this example, the document uses one of the CSS rules from the stylesheet, but the stylesheet is (incorrectly) placed at the bottom. When the page loads progressively the text is displayed first, followed by the images as they arrive. Finally, when the stylesheet is successfully downloaded and parsed, the already-rendered text and images are redrawn using the new styles. This is the "flash of unstyled content" in action. It should be avoided.The blank white screen is the browser's attempt to be forgiving to frontend engineers who mistakenly put their stylesheets too far down in the document. The blank white screen is the foil of the FOUC problem. The browser can delay rendering until all the stylesheets are downloaded, causing the blank white screen. By contrast, the browser can render progressively and risk flashing the user. Neither choice is ideal.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - What's a Frontend Engineer to Do?
- InhaltsvorschauSo how can you avoid both the blank white screen and the flash of unstyled content?In the "CSS Flash of Unstyled Content" example, the flash doesn't always happen; it depends on your browser and how you load the page. Earlier in this chapter, I explained that the blank white screen happens in Internet Explorer only when the page is loaded in a new window, as a reload, or as a home page. In these cases, Internet Explorer chooses the blank white screen. However, if you click on a link, use a bookmark, or type a URL, Internet Explorer chooses the second alternative: risking FOUC.Firefox is more consistent—it always chooses the second alternative (FOUC). All the examples behave identically in Firefox: they render progressively. For the first three examples, Firefox's behavior works to the user's benefit because the stylesheet is not required for rendering the page, but in the "CSS Flash of Unstyled Content" example, the user is less fortunate. The user experiences the FOUC problem precisely because Firefox renders progressively.When browsers behave differently, what's a frontend engineer to do?You can find the answer in the HTML specification (http://www.w3.org/tr/html4/struct/links.html#h-12.3):Unlike A, [LINK] may only appear in the HEAD section of a document, although it may appear any number of times.Browsers have a history of supporting practices that violate the HTML specification in order to make older, sloppier web pages work, but when it comes to handling the placement of stylesheets, Internet Explorer and Firefox are nudging the web development community to follow the specification. Pages that violate the specification (by putting the
LINKoutside of theHEADsection) still render, but risk a degraded user experience.In their effort to improve one of the most visited pages on the Web, the Yahoo! portal team initially made it worse by moving the stylesheet to the bottom of the page. They found the optimal solution by following the HTML specification and leaving it at the top. Neither of the alternatives—the blank white screen or flash of unstyled content—are worth the risk. If you have a stylesheet that's not required to render the page, with some extra effort you can load it dynamically after the document loads, as described in the section "" in . Otherwise, whether your stylesheets are necessary to render the page or not, there's one rule to follow.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 8: Rule 6: Put Scripts at the Bottom
- Inhaltsvorschaudescribed how stylesheets near the bottom of the page prohibit progressive rendering, and how moving them to the document
HEADeliminates the problem. Scripts (external JavaScript files) pose a similar problem, but the solution is just the opposite: it's better to move scripts from the top of the page to the bottom (when possible). This enables progressive rendering and achieves greater download parallelization. Let's first look at an example of these problems.The best way to demonstrate the issues with scripts is by using an example that has a script in the middle of the page.- Scripts in the Middle
This script is programmed to take 10 seconds to load, so it's easy to see the problem—the bottom half of the page takes about 10 seconds to appear (see the section "" in for an explanation of how components are configured to have specific load times). This occurs because the script blocks parallel downloading. We'll come back to this problem after a review of how browsers download in parallel.The other problem with the example page has to do with progressive rendering. When using stylesheets, progressive rendering is blocked until all stylesheets have been downloaded. That's why it's best to move stylesheets to the documentHEAD, so they are downloaded first and rendering isn't blocked. With scripts, progressive rendering is blocked for all content below the script. Moving scripts lower in the page means more content is rendered progressively.The biggest impact on response time is the number of components in the page. Each component generates an HTTP request when the cache is empty, and sometimes even when the cache is primed. Knowing that the browser performs HTTP requests in parallel, you may ask why the number of HTTP requests affects response time. Can't the browser download them all at once?The explanation goes back to the HTTP/1.1 specification, which suggests that browsers download two components in parallel per hostname (http://www.w3.org/protocols/rfc2616/rfc2616-sec8.html#sec8.1.4). Many web pages download all their components from a single hostname. Viewing these HTTP requests reveals a stair-step pattern, as shown in .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Problems with Scripts
- InhaltsvorschauThe best way to demonstrate the issues with scripts is by using an example that has a script in the middle of the page.
- Scripts in the Middle
This script is programmed to take 10 seconds to load, so it's easy to see the problem—the bottom half of the page takes about 10 seconds to appear (see the section "" in for an explanation of how components are configured to have specific load times). This occurs because the script blocks parallel downloading. We'll come back to this problem after a review of how browsers download in parallel.The other problem with the example page has to do with progressive rendering. When using stylesheets, progressive rendering is blocked until all stylesheets have been downloaded. That's why it's best to move stylesheets to the documentHEAD, so they are downloaded first and rendering isn't blocked. With scripts, progressive rendering is blocked for all content below the script. Moving scripts lower in the page means more content is rendered progressively.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Parallel Downloads
- InhaltsvorschauThe biggest impact on response time is the number of components in the page. Each component generates an HTTP request when the cache is empty, and sometimes even when the cache is primed. Knowing that the browser performs HTTP requests in parallel, you may ask why the number of HTTP requests affects response time. Can't the browser download them all at once?The explanation goes back to the HTTP/1.1 specification, which suggests that browsers download two components in parallel per hostname (http://www.w3.org/protocols/rfc2616/rfc2616-sec8.html#sec8.1.4). Many web pages download all their components from a single hostname. Viewing these HTTP requests reveals a stair-step pattern, as shown in .
Figure : Downloading two components in parallelIf a web page evenly distributed its components across two hostnames, the overall response time would be about twice as fast. The HTTP requests would behave in the pattern shown in , with four components downloaded in parallel (two per hostname). Tto give a visual cue as to how much faster this page loads, the horizontal width of the box is the same as in .
Figure : Downloading four components in parallelLimiting parallel downloads to two per hostname is a guideline. By default, both Internet Explorer and Firefox follow the guideline, but users can override this default behavior. Internet Explorer stores the value in the Registry Editor.You can modify this default setting in Firefox by using thenetwork.http.max-persistent-connections-per-serversetting in the about:config page. It's interesting to note that for HTTP/1.0, Firefox's default is to download eight components in parallel per hostname. shows that Firefox's settings for HTTP/1.0 result in the fastest response time for this hypothetical page. It's even faster than that shown in , even though only one hostname is used.
Figure : Downloading eight components in parallel (default for Firefox HTTP/1.0)Most web sites today use HTTP/1.1, but the idea of increasing parallel downloads beyond two per hostname is intriguing. Instead of relying on users to modify their browser settings, frontend engineers could simply use CNAMEs (DNS aliases) to split their components across multiple hostnames. Maximizing parallel downloads doesn't come without a cost. Depending on your bandwidth and CPU speed, too many parallel downloads can degrade performance. Research at Yahoo! shows that splitting components across two hostnames leads to better performance than using 1, 4, or 10 hostnames.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Scripts Block Downloads
- InhaltsvorschauThe benefits of downloading components in parallel are clear. However, parallel downloading is actually disabled while a script is downloading—the browser won't start any other downloads, even on different hostnames. One reason for this behavior is that the script may use
document.writeto alter the page content, so the browser waits to make sure the page is laid out appropriately.Another reason that the browser blocks parallel downloads when scripts are being loaded is to guarantee that the scripts are executed in the proper order. If multiple scripts were downloaded in parallel, there's no guarantee the responses would arrive in the order specified. For example, if the last script was smaller than scripts that appear earlier on the page, it might return first. If there were dependencies between the scripts, executing them out of order would result in JavaScript errors. The following example demonstrates how scripts block parallel downloads.- Scripts Block Downloads
This page contains the following components in this order:- An image from host1
- An image from host2
- A script from host1 that takes 10 seconds to load
- An image from host1
- An image from host2
Given the description of how browsers download in parallel, you would expect that the two images from host2 would be downloaded in parallel, along with the first two components from host1. shows what really happens.
Figure : Scripts block downloadsIn both Internet Explorer and Firefox, the browser starts by downloading the first image from host1 and host2. Next, the script from host1 is downloaded. Here's where the unexpected behavior occurs. While the script is being downloaded (exaggerated to 10 seconds to illustrate the point), the second image from host1 and host2 are blocked from downloading. Not until the script is finished loading are the remaining components in the page downloaded.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Worst Case: Scripts at the Top
- InhaltsvorschauAt this point, the effects that scripts can have on web pages are clear:
- Content below the script is blocked from rendering.
- Components below the script are blocked from being downloaded.
If scripts are put at the top of the page, as they usually are, everything in the page is below the script, and the entire page is blocked from rendering and downloading until the script is loaded. Try out the following example.- Scripts at the Top
Because this entire page is blocked from rendering, it results in the blank white screen phenomenon described in . Progressive rendering is critical for a good user experience, but slow scripts delay the feedback users crave. Also, the reduction of parallelized downloads delays how quickly images are displayed in the page. shows how the components in the page are downloaded later than desired.
Figure : Script at the top blocks the entire pageEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Best Case: Scripts at the Bottom
- InhaltsvorschauThe best place to put scripts is at the bottom of the page. The page contents aren't blocked from rendering, and the viewable components in the page are downloaded as early as possible. shows how the long request for the script has less of an effect on the page when it is placed at the bottom. You can see this by visiting the following example.
- Scripts at the Bottom
Figure : Scripts at the bottom have the least impactThe benefit is really highlighted by viewing the pages with scripts at the top versus scripts at the bottom side-by-side. You can see this in the following example.- Scripts Top vs. Bottom
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Putting It in Perspective
- InhaltsvorschauThese examples use a script that takes 10 seconds to download. Hopefully, the delay isn't as long for any scripts you use, but it is possible for a script to take longer than expected and for the user's bandwidth to affect the response time of a script. The effect that scripts have on your pages might be less than shown here, but it could still be noticeable. Having multiple scripts in your page compounds the problem.In some situations, it's not easy to move scripts to the bottom. If, for example, the script uses
document.writeto insert part of the page's content, it can't be moved lower in the page. There might also be scoping issues. In many cases, there are ways to work around these situations.An alternative suggestion that comes up often is to use deferred scripts. TheDEFERattribute indicates that the script does not containdocument.write, and is a clue to browsers that they can continue rendering. You can see this in the following example.- Deferred Scripts
Unfortunately, in Firefox, even deferred scripts block rendering and parallel downloads. In Internet Explorer, components lower in the page are downloaded slightly later. If a script can be deferred, it can also be moved to the bottom of the page. That's the best thing to do to speed up your web pages.Move scripts to the bottom of the page.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 9: Rule 7: Avoid CSS Expressions
- InhaltsvorschauCSS expressions are a powerful (and dangerous) way to set CSS properties dynamically. They're supported in Internet Explorer version 5 and later. Let's start with a conventional CSS rule for setting the background color:
background-color: #B8D4FF;
For a more dynamic page, the background color could be set to alternate every hour using CSS expressions.background-color: expression( (new Date()).getHours( )%2 ? "#B8D4FF" : "#F08A00" );
As shown here, theexpressionmethod accepts a JavaScript expression. The CSS property is set to the result of evaluating the JavaScript expression.Theexpressionmethod is simply ignored by other browsers, so it is a useful tool for setting properties in Internet Explorer to create a consistent experience across browsers. For example, Internet Explorer does not support themin-widthproperty. CSS expressions are one way to solve this problem. The following example ensures that a page width is always at least 600 pixels, using an expression that Internet Explorer respects and a static setting honored by other browsers:width: expression( document.body.clientWidth < 600 ? "600px" : "auto" ); min-width: 600px;
Most browsers ignore thewidthproperty here because they don't support CSS expressions and instead use themin-widthproperty. Internet Explorer ignores themin-widthproperty and instead sets thewidthproperty dynamically based on the width of the document. CSS expressions are re-evaluated when the page changes, such as when it is resized. This ensures that as the user resizes his browser, the width is adjusted appropriately. The frequency with which CSS expressions are evaluated is what makes them work, but it is also what makes CSS expressions bad for performance.The problem with expressions is that they are evaluated more frequently than most people expect. Not only are they evaluated whenever the page is rendered and resized, but also when the page is scrolled and even when the user moves the mouse over the page. Adding a counter to the CSS expression allows us to keep track of when and how often a CSS expression is evaluated.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Updating Expressions
- InhaltsvorschauThe problem with expressions is that they are evaluated more frequently than most people expect. Not only are they evaluated whenever the page is rendered and resized, but also when the page is scrolled and even when the user moves the mouse over the page. Adding a counter to the CSS expression allows us to keep track of when and how often a CSS expression is evaluated.
- Expression Counter
The CSS expression counter example has the following CSS rule:P { width: expression( setCntr( ), document.body.clientWidth<600 ? "600px" : "auto" ); min-width: 600px; border: 1px solid; }ThesetCntr( )function increments a global variable and writes the value in the text field in the page. There are 10 paragraphs in the page. Loading the page executes the CSS expression 40 times. Subsequent to that, the CSS expression is evaluated 10 times for various events including resize, scrolling, and mouse movements. Moving the mouse around the page can easily generate more than 10,000 evaluations. The danger of CSS expressions is evident in this example. Worst of all, clicking in the text input field locks up Internet Explorer, and you have to kill the process.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Working Around the Problem
- InhaltsvorschauMost CSS experts are familiar with CSS expressions and how to avoid the pitfalls highlighted by the previous example. Two techniques for avoiding problems created by CSS expressions are creating one-time expressions and using event handlers instead of CSS expressions.If the CSS expression has to be evaluated only once, it can overwrite itself as part of its execution. The background style defined at the beginning of this chapter is a good candidate for this approach:
<style> P { background-color: expression( altBgcolor(this) ); } </style> <script type="text/javascript"> function altBgcolor(elem) { elem.style.backgroundColor = (new Date()).getHours( )%2 ? "#F08A00" : "#B8D4FF"; } </script>The CSS expression calls thealtBgcolor( )function, which sets the style'sbackground-colorproperty to an explicit value, and this replaces the CSS expression. This style is associated with the 10 paragraphs in the page. Even after resizing, scrolling, and moving the mouse around the page, the CSS expression is evaluated only 10 times, much better than the tens of thousands in the previous example.- One-Time Expressions
In most situations where I've seen CSS expressions used, it was possible to find an alternative that didn't require them. CSS expressions benefit from being automatically tied to events in the browser, but that's also their downfall. Instead of using CSS expressions, the frontend engineer can do the "heavy lifting" by tying the desired dynamic behavior to the appropriate event using event handlers. This avoids the evaluation of the expression during unrelated events. The event handler example demonstrates a fix to themin-widthproblem by setting the style'swidthproperty with theonresizeevent, avoiding tens of thousands of unnecessary evaluations during mouse movements, scrolling, etc.- Event Handler
This example uses thesetMinWidth( )function to resize all paragraph elements when the browser is resized:function setMinWidth( ) { setCntr( ); var aElements = document.getElementsByTagName("p"); for ( var i = 0; i < aElements.length; i++ ) { aElements[i].runtimeStyle.width = ( document.body.clientWidth<600 ? "600px" : "auto" ); } } if ( −1 != navigator.userAgent.indexOf("MSIE") ) { window.onresize = setMinWidth; }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Conclusion
- InhaltsvorschauThis is one of the few rules that addresses performance of the page after it has been loaded, which is generally when CSS expressions cause problems. However, in some cases, CSS expressions can affect the load time of a page, too. One property at Yahoo! had a CSS expression that caused a 20-second delay during the initial rendering of the page. This result was unexpected and took a while to diagnose. Similarly, who would have thought the CSS expression used in the "Expression Counter" example would cause Internet Explorer to lock up if the user clicked in a text field? A full discussion of complicated CSS incompatibilities, such as
min-widthandlocation: fixed, is beyond the scope of this book, and that's the point—using CSS expressions without a deep understanding of the underlying implications is dangerous.Avoid CSS expressions.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 10: Rule 8: Make JavaScript and CSS External
- InhaltsvorschauMany of the performance rules in this book deal with how external components are managed, such as serving them via a CDN (Rule 2), making sure they have a far future
Expiresheader (Rule 3), and compressing their contents (Rule 4). However, before these considerations arise, you should ask a more basic question: should JavaScript and CSS be contained in external files or inlined in the page itself? As we'll see, using external files is generally better.Let's first start with the tradeoffs in placing JavaScript and CSS inline versus using external files.I have generated two examples that demonstrate how inlining JavaScript and CSS results in faster response times than making them external files.- Inlined JS and CSS
- External JS and CSS
The inline example involves one HTML document that is 87K, with all of the JavaScript and CSS in the page itself. The external example contains an HTML document (7K), one stylesheet (59K), and three scripts (1K, 11K, and 9K) for a total of 87K. Although the total amount of data downloaded is the same, the inline example is 30–50% faster than the external example. This is primarily because the external example suffers from the overhead of multiple HTTP requests (see about the importance of minimizing HTTP requests). The external example even benefits from the stylesheet and scripts being downloaded in parallel, but the difference of one HTTP request compared to five is what makes the inline example faster.Despite these results, using external files in the real world generally produces faster pages. This is due to a benefit of external files that is not captured by these examples: the opportunity for the JavaScript and CSS files to be cached by the browser. HTML documents, at least those that contain dynamic content, are typically not configured to be cached. When this is the case (when the HTML documents are not cached), the inline JavaScript and CSS is downloaded every time the HTML document is requested. On the other hand, if the JavaScript and CSS are in external files cached by the browser, the size of the HTML document is reduced without increasing the number of HTTP requests.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Inline vs. External
- InhaltsvorschauLet's first start with the tradeoffs in placing JavaScript and CSS inline versus using external files.I have generated two examples that demonstrate how inlining JavaScript and CSS results in faster response times than making them external files.
- Inlined JS and CSS
- External JS and CSS
The inline example involves one HTML document that is 87K, with all of the JavaScript and CSS in the page itself. The external example contains an HTML document (7K), one stylesheet (59K), and three scripts (1K, 11K, and 9K) for a total of 87K. Although the total amount of data downloaded is the same, the inline example is 30–50% faster than the external example. This is primarily because the external example suffers from the overhead of multiple HTTP requests (see about the importance of minimizing HTTP requests). The external example even benefits from the stylesheet and scripts being downloaded in parallel, but the difference of one HTTP request compared to five is what makes the inline example faster.Despite these results, using external files in the real world generally produces faster pages. This is due to a benefit of external files that is not captured by these examples: the opportunity for the JavaScript and CSS files to be cached by the browser. HTML documents, at least those that contain dynamic content, are typically not configured to be cached. When this is the case (when the HTML documents are not cached), the inline JavaScript and CSS is downloaded every time the HTML document is requested. On the other hand, if the JavaScript and CSS are in external files cached by the browser, the size of the HTML document is reduced without increasing the number of HTTP requests.The key factor, then, is the frequency with which external JavaScript and CSS components are cached relative to the number of HTML documents requested. This factor, although difficult to quantify, can be gauged using the following metrics.The fewer page views per user, the stronger the argument for inlining JavaScript and CSS. Imagine that a typical user visits your site once per month. Between visits, it's likely that any external JavaScript and CSS files have been purged from the browser's cache, even if the components have a far futureEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Typical Results in the Field
- InhaltsvorschauIn analyzing the tradeoffs between inlining versus using external files, the key is the frequency with which external JavaScript and CSS components are cached relative to the number of HTML documents requested. In the previous section, I described three metrics (page views, empty cache vs. primed cache, and component reuse) that can help you determine the best option. The right answer for any specific web site depends on these metrics.Many web sites fall in the middle of these metrics. They get 5–15 page views per user per month, with 2–5 page views per user per session. Empty cache visits are in the same range as Yahoo!: 40–60% of unique users per day have a primed cache, and 75–85% of page views per day are performed with a primed cache. There's a fair amount of JavaScript and CSS reuse across pages, resulting in a handful of files that cover every major page type.For sites that have these metrics, the best solution is generally to deploy the JavaScript and CSS as external files. This is demonstrated by the example where the external components can be cached by the browser. Loading this page repeatedly and comparing the results to those of the first example, "Inlined JS and CSS," shows that using external files with a far future
Expiresheader is the fastest approach.- Cacheable External JS and CSS
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Home Pages
- InhaltsvorschauThe only exception I've seen where inlining is preferable is with home pages. A home page is the URL chosen as the browser's default page, such as Yahoo! home page (http://www.yahoo.com) and My Yahoo! (http://my.yahoo.com). Let's look at the three metrics from the perspective of home pages:
- Page views
- Home pages have a high number of page views per month. By definition, whenever the browser is opened, the home page is visited. However, there is often only one page view per session.
- Empty cache vs. primed cache
- The primed cache percentage might be lower than other sites. For security reasons, many users elect to clear the cache every time they close the browser. The next time they open the browser it generates an empty cache page view of the home page.
- Component reuse
- The reuse rate is low. Many home pages are the only page a user visits on the site, so there is really no reuse.
Analyzing these metrics, there's an inclination toward inlining over using external files. Home pages have one more factor that tips the scale toward inlining: they have a high demand for responsiveness, even in the empty cache scenario. If a company decides to launch a campaign encouraging users to set their home pages to the company's site, the last thing they want is a slow home page. For the company's home page campaign to succeed, the page must be fast.There's no single answer that applies to all home pages. The factors highlighted here must be evaluated for the home page in question. If inlining is the right answer, you'll find helpful information in the next section, which describes two techniques that have the benefit of inlining while taking advantage of external files (when possible).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Best of Both Worlds
- InhaltsvorschauEven if all the factors point to inlining, it still feels inefficient to add all that JavaScript and CSS to the page and not take advantage of the browser's cache. Two techniques are described here that allow you to gain the benefits of inlining, as well as caching external files.Some home pages, like Yahoo! home page and My Yahoo!, typically have only one page view per session. However, that's not the case for all home pages. Yahoo! Mail is a good example of a home page that often leads to secondary page views (pages that are accessed after the initial page, such as those for viewing or composing email messages).For home pages that are the first of many page views, we want to inline the JavaScript and CSS for the home page, but leverage external files for all secondary page views. This is accomplished by dynamically downloading the external components in the home page after it has completely loaded (via the
onloadevent). This places the external files in the browser's cache in anticipation of the user continuing on to other pages.- Post-Onload Download
The post-onload download JavaScript code associates thedoOnloadfunction with the document'sonloadevent. After a one-second delay (to make sure the page is completely rendered), the appropriate JavaScript and CSS files are downloaded. This is done by creating the appropriate DOM elements (scriptandlink, respectively) and assigning the specific URL:<script type="text/javascript"> function doOnload( ) { setTimeout("downloadComponents( )", 1000); } window.onload = doOnload; // Download external components dynamically using JavaScript. function downloadComponents( ) { downloadJS("http://stevesouders.com/hpws/testsma.js"); downloadCSS("http://stevesouders.com/hpws/testsm.css"); } // Download a script dynamically. function downloadJS(url) { var elem = document.createElement("script"); elem.src = url; document.body.appendChild(elem); } // Download a stylesheet dynamically. function downloadCSS(url) { var elem = document.createElement("link"); elem.rel = "stylesheet"; elem.type = "text/css"; elem.href = url; document.body.appendChild(elem); } </script>Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 11: Rule 9: Reduce DNS Lookups
- InhaltsvorschauThe Internet is based on finding servers through IP addresses. Because IP addresses are hard to remember, URLs typically contain hostnames instead, but the IP address is still necessary for the browser to make its request. That's the role of the Domain Name System (DNS). DNS maps hostnames to IP addresses, just as phonebooks map people's names to their phone numbers. When you type www.yahoo.com into your browser, a DNS resolver is contacted by the browser and returns that server's IP address.This explanation highlights another benefit of DNS—a layer of indirection between URLs and the actual servers that host them. If a server is replaced with one that has a different IP address, DNS allows users to use the same hostname to connect to the new server. Or, as is the case with www.yahoo.com, multiple IP addresses can be associated with a hostname, providing a high degree of redundancy for a web site.However, DNS has a cost. It typically takes 20–120 milliseconds for the browser to look up the IP address for a given hostname. The browser can't download anything from this hostname until the DNS lookup is completed. The response time depends on the DNS resolver (typically provided by your ISP), the load of requests on it, your proximity to it, and your bandwidth speed. After reviewing how DNS works from the browser's perspective, I'll describe what you can do to reduce the amount of time your pages spend doing DNS lookups.DNS lookups are cached for better performance. This caching can occur on a special caching server maintained by the user's ISP or local area network, but here we'll explore DNS caching on the individual user's computer. As shown in , after a user requests a hostname, the DNS information remains in the operating system's DNS cache (the "DNS Client service" on Microsoft Windows), and further requests for that hostname don't require more DNS lookups, at least not for a while.
Figure : DNS caching from the browser's perspectiveSimple enough? Hold on a minute—most browsers have their own caches, separate from the operating system's cache. As long as the browser keeps a DNS record in its own cache, it doesn't bother the operating system with a request for the record. Only after the browser's cache discards the record does it ask the operating system for the address—and then the operating system either satisfies the request out of its cache or sends a request to a remote server, which is where potential slowdowns occur.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - DNS Caching and TTLs
- InhaltsvorschauDNS lookups are cached for better performance. This caching can occur on a special caching server maintained by the user's ISP or local area network, but here we'll explore DNS caching on the individual user's computer. As shown in , after a user requests a hostname, the DNS information remains in the operating system's DNS cache (the "DNS Client service" on Microsoft Windows), and further requests for that hostname don't require more DNS lookups, at least not for a while.
Figure : DNS caching from the browser's perspectiveSimple enough? Hold on a minute—most browsers have their own caches, separate from the operating system's cache. As long as the browser keeps a DNS record in its own cache, it doesn't bother the operating system with a request for the record. Only after the browser's cache discards the record does it ask the operating system for the address—and then the operating system either satisfies the request out of its cache or sends a request to a remote server, which is where potential slowdowns occur.To make things yet more complicated, designers realize that IP addresses change and that caches consume memory. Therefore, the DNS records have to be periodically flushed from the cache, and several different configuration settings determine how often they are discarded.First, the server has a say in how long records should be cached. The DNS record returned from a lookup contains a time-to-live (TTL) value. This tells the client how long the record can be cached.Although operating system caches respect the TTL, browsers often ignore it and set their own time limits. Furthermore, the Keep-Alive feature of the HTTP protocol, discussed in , can override both the TTL and the browser's time limit. In other words, as long as the browser and the web server are happily communicating and keeping their TCP connection open, there's no reason for a DNS lookup.Browsers put a limit on the number of DNS records cached, regardless of the time the records have been in the cache. If the user visits many different sites with different domain names in a short period of time, earlier DNS records are discarded and the domain must be looked up again.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Browser's Perspective
- InhaltsvorschauAs discussed earlier in the "" section, a lot of independent variables determine whether a particular browser request for a hostname makes a remote DNS request. There is a DNS specification (http://tools.ietf.org/html/rfc1034), but it gives clients flexibility in how the DNS cache works. I'll focus on Internet Explorer and Firefox on Microsoft Windows, since they are the most popular platforms.The DNS cache on Microsoft Windows is managed by the DNS Client service. You can view and flush the DNS Client service using the
ipconfigcommand:ipconfig /displaydns ipconfig /flushdns
Rebooting also clears the DNS Client service cache. In addition to the DNS Client service, Internet Explorer and Firefox browsers have their own DNS caches. Restarting the browser clears the browser cache, but not the DNS Client service cache.Internet Explorer's DNS cache is controlled by three registry settings:DnsCacheTimeout,KeepAliveTimeout, andServerInfoTimeOut, which can be created in the following registry key:HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\InternetSettings\
There are two Microsoft Support articles describing how these settings affect the DNS cache. These articles report the following default values for these settings:DnsCacheTimeout: 30 minutesKeepAliveTimeout: 1 minuteServerInfoTimeOut: 2 minutes
What's implied (but not explained very well) is that DNS server TTL values less than 30 minutes have little effect on how frequently the browser does DNS lookups. Once the browser caches a DNS record, it is used for 30 minutes. If there is an error, the DNS lookup is refreshed sooner than that; under normal conditions, a short (under 30 minutes) TTL value won't increase the number of DNS lookups made in Internet Explorer.The role that Keep-Alive plays is also important. By default, a persistent TCP connection is used until it has been idle for one minute. Because the connection persists, a DNS lookup is not required (the benefits of Keep-Alive are discussed in ). This is an additional benefit—Keep-Alive avoids repeated DNS lookups by reusing the existing connection.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Reducing DNS Lookups
- InhaltsvorschauWhen the client's DNS cache is empty (for both the browser and the operating system), the number of DNS lookups is equal to the number of unique hostnames in the web page. This includes the hostnames used in the page's URL, images, script files, stylesheets, Flash objects, etc. Reducing the number of unique hostnames reduces the number of DNS lookups. Google (http://www.google.com) is the preeminent example of this, with only one DNS lookup necessary for the page.Reducing the number of unique hostnames has the potential to reduce the amount of parallel downloading that takes place in the page. Avoiding DNS lookups cuts response times, but reducing parallel downloads may increase response times. As described in in the section "," some amount of parallelization is good, even if it increases the number of hostnames. In the case of Google.com, there are only two components in the page. Because components are downloaded two per hostname in parallel, using one hostname minimizes the number of possible DNS lookups while maximizing parallel downloads.Most pages today have a dozen or more components—not nearly as lean as Google. My guideline is to split these components across at least two but no more than four hostnames. This results in a good compromise between reducing DNS lookups and allowing a high degree of parallel downloads.The advantage of using Keep-Alive, described in , is that it reuses an existing connection, thereby improving response times by avoiding TCP/IP overhead. As described here, making sure your servers support Keep-Alive also reduces DNS lookups, especially for Firefox users.Reduce DNS lookups by using Keep-Alive and fewer domains.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 12: Rule 10: Minify JavaScript
- InhaltsvorschauJavaScript, being an interpreted language, is great for building web pages. Interpreted languages excel when developing user interfaces where rapid prototyping is the norm. Without a compilation step, though, the responsibility falls on the frontend engineer to optimize the JavaScript before final deployment. One aspect of this, gzipping, is discussed in . In this chapter, I describe another step that should be integrated into the JavaScript deployment process: minification.Minification is the practice of removing unnecessary characters from code to reduce its size, thereby improving load times. When code is minified, all comments are removed, as well as unneeded whitespace characters (space, newline, and tab). In the case of JavaScript, this improves response time performance because the size of the downloaded file is reduced.shows how many of the 10 top U.S. web sites practice JavaScript minification— 4 out of 10 minify their JavaScript code.
Table : Minification practices across 10 top web sites Web siteExternal scripts minified?NoNoNoYesYesYesNoNoYesNoLet's look at what the others could have saved if they had minified. But first, I need to mention a more aggressive alternative to minification: obfuscation.Obfuscation is an alternative optimization that can be applied to source code. Like minification, it removes comments and whitespace, but it also munges the code. As part of munging, function and variable names are converted into smaller strings making the code more compact, as well as harder to read. This is typically done to make it more difficult to reverse-engineer the code, but munging can help performance because it reduces the code size beyond what is achieved by minification.Assuming that thwarting reverse-engineering is not your objective, the question arises about whether to minify or obfuscate. Minification is a safe, fairly straightforward process. Obfuscation, on the other hand, is more complex. There are three main drawbacks to obfuscating your JavaScript code:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Minification
- InhaltsvorschauMinification is the practice of removing unnecessary characters from code to reduce its size, thereby improving load times. When code is minified, all comments are removed, as well as unneeded whitespace characters (space, newline, and tab). In the case of JavaScript, this improves response time performance because the size of the downloaded file is reduced.shows how many of the 10 top U.S. web sites practice JavaScript minification— 4 out of 10 minify their JavaScript code.
Table : Minification practices across 10 top web sites Web siteExternal scripts minified?NoNoNoYesYesYesNoNoYesNoLet's look at what the others could have saved if they had minified. But first, I need to mention a more aggressive alternative to minification: obfuscation.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Obfuscation
- InhaltsvorschauObfuscation is an alternative optimization that can be applied to source code. Like minification, it removes comments and whitespace, but it also munges the code. As part of munging, function and variable names are converted into smaller strings making the code more compact, as well as harder to read. This is typically done to make it more difficult to reverse-engineer the code, but munging can help performance because it reduces the code size beyond what is achieved by minification.Assuming that thwarting reverse-engineering is not your objective, the question arises about whether to minify or obfuscate. Minification is a safe, fairly straightforward process. Obfuscation, on the other hand, is more complex. There are three main drawbacks to obfuscating your JavaScript code:
- Bugs
- Because obfuscation is more complex, there's a higher probability of introducing errors into the code as a result of the obfuscation process itself.
- Maintenance
- Since obfuscators change JavaScript symbols, any symbols that should not be changed (for example, API functions) have to be tagged so that the obfuscator leaves them unaltered.
- Debugging
- Obfuscated code is more difficult to read. This makes debugging problems in your production environment more difficult.
Although I've never seen problems introduced from minification, I have seen bugs caused by obfuscation. Given the large amount of JavaScript maintained at Yahoo!, my guidelines recommend minification over obfuscation. The ultimate decision has to consider the additional size reductions achieved from obfuscation. In the next section, we'll do some real minifying and obfuscating.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Savings
- InhaltsvorschauThe most popular tool for minifying JavaScript code is JSMin (http://crockford.com/javascript/jsmin), developed by Douglas Crockford, a fellow Yahoo!. The JSMin source code is available in C, C#, Java, JavaScript, Perl, PHP, Python, and Ruby. The tool of choice is less clear in the area of JavaScript obfuscation. Dojo Compressor (renamed ShrinkSafe and moved to http://dojotoolkit.org/docs/shrinksafe) is the one I've seen used the most. For the purposes of our comparison, I used these two tools. As a demonstration, let's use these tools on event.js from the Yahoo! User Interface (YUI) library (http://developer.yahoo.com/yui). The source code for the first function follows:
YAHOO.util.CustomEvent = function(type, oScope, silent, signature) { this.type = type; this.scope = oScope || window; this.silent = silent; this.signature = signature || YAHOO.util.CustomEvent.LIST; this.subscribers = []; if (!this.silent) { } var onsubscribeType = "_YUICEOnSubscribe"; if (type !== onsubscribeType) { this.subscribeEvent = new YAHOO.util.CustomEvent(onsubscribeType, this, true); } };The same function passed through JSMin has all unneeded whitespace removed:YAHOO.util.CustomEvent=function(type,oScope,silent,signature){this.type=type;this. scope=oScope||window;this.silent=silent;this.signature=signature||YAHOO.util. CustomEvent.LIST;this.subscribers=[];if(!this.silent){} var onsubscribeType="_YUICEOnSubscribe";if(type!==onsubscribeType){this.subscribeEv ent=new YAHOO.util.CustomEvent(onsubscribeType,this,true);}};Dojo Compressor removes most whitespace, but additionally shortens variable names. Notice how_1has replacedtypeas the first parameter to theCustomEventfunction:YAHOO.util.CustomEvent=function(_1,_2,_3,_4){ this.type=_1; this.scope=_2||window; this.silent=_3; this.signature=_4||YAHOO.util.CustomEvent.LIST; this.subscribers=[]; if(!this.silent){ } var _5="_YUICEOnSubscribe"; if(_1!==_5){ this.subscribeEvent=new YAHOO.util.CustomEvent(_5,this,true); } };shows some potential savings for the six companies who didn't minify their JavaScript files. I downloaded the JavaScript files used on each site's home page. The table shows the original size of each site's JavaScript files, as well as the size reductions gained by running them through JSMin and Dojo Compressor. On average, JSMin reduced the size of JavaScript files by 21%, while Dojo Compressor achieved a 25% reduction.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Examples
- InhaltsvorschauTo demonstrate the benefits of minification and obfuscation, I have generated two scripts of different sizes: a small script (50K) and a large script (377K). The small script drops to 13K after minification and 12K after obfuscation. The large script is reduced to 129K after minification and 123K after obfuscation. Testing both files under these three states results in the following six examples.
- Small Script Normal
- Small Script Minified
- Small Script Obfuscated
- Large Script Normal
- Large Script Minified
- Large Script Obfuscated
As shown in , minification and obfuscation perform about the same, but are significantly faster than the normal case. For the small script, minification and obfuscation are 100–110ms (17–19%) faster than the normal case. For the large script, minification and obfuscation are 331–341ms (30–31%) faster than the normal case.Table : Response times for minified and obfuscated scripts Script sizeNormalMinifiedObfuscatedSmall (50K)581 ms481 ms471 msLarge (377K)1092 ms761 ms751 msAs mentioned in the previous section, the difference between minification and obfuscation decreases when combined with gzip compression; this is demonstrated by these examples. Minifying scripts reduces response times without carrying the risks that come with obfuscation.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Icing on the Cake
- InhaltsvorschauThere are a couple other ways to squeeze waste out of your JavaScript.The discussion thus far has focused on external JavaScript files. Inline JavaScript blocks should also be minified, though this practice is less evident on today's web sites. shows that although 4 of the 10 top web sites minify their external scripts, only 3 minify their inline scripts.
Table : Inline minification practices across 10 top web sites Web siteExternal scripts minified?Inline scripts minified?nonononononoyesnoyesyesyesyesnonononoyesyesnonoIn practice, minifying inline scripts is easier than minifying external scripts. Whatever page generation platform you use (PHP, Python, Perl CGI, etc.), there is probably a version of JSMin that can be integrated with it. Once the functionality is available, all inlined JavaScript can be minified before being echoed to the HTML document.Rule 4 stresses the importance of compressing content and recommends using gzip to accomplish this, resulting in a typical size reduction of 70%. Gzip compression decreases file sizes more than minification—that's why it's in Rule 4 and this is Rule 10. I've heard people question whether minification is even worthwhile if gzip compression has already been enabled.is similar to , except the responses are gzipped. When gzipped, the average size of the JavaScript payload drops from 85K (see ) to 23K (see ), a reduction of 73%. It's reassuring to see that the guidelines of Rule 4 hold true for these six web sites. shows that minifying the files in addition to gzipping them reduces the payload by an average of 4K (20%) over gzip alone. It's interesting that obfuscation and gzip perform about the same as minification and gzip, another reason to just stick with minification and avoid the additional risks of obfuscation.Table : Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 13: Rule 11: Avoid Redirects
- InhaltsvorschauA redirect is used to reroute users from one URL to another. There are different kinds of redirects—301 and 302 are the most popular. Redirects are usually done for HTML documents, but they may also be used when requesting components in the page (images, scripts, etc.). There are different reasons for implementing redirects, including web site redesign, tracking traffic flow, counting ad impressions, and creating URLs that are easier for users to remember. We'll examine all of these aspects in this chapter, but the main thing to remember is that redirects make your pages slower.When web servers return a redirect to the browser, the response has a status code in the 3xx range. This indicates that further action is required of the user agent in order to fulfill the request. There are several 3xx status codes:
- 300 Multiple Choices (based on Content-Type)
- 301 Moved Permanently
- 302 Moved Temporarily (a.k.a. Found)
- 303 See Other (clarification of 302)
- 304 Not Modified
- 305 Use Proxy
- 306 (no longer used)
- 307 Temporary Redirect (clarification of 302)
"304 Not Modified" is not really a redirect—it's used in response to conditional GET requests to avoid downloading data that is already cached by the browser, as explained in . Status code 306 is deprecated.The 301 and 302 status codes are the ones used most often. Status codes 303 and 307 were added in the HTTP/1.1 specification to clarify the (mis)use of 302, but the adoption of 303 and 307 is almost nonexistent, as most web sites continue to use 302. Here's an example of the headers in a 301 response.HTTP/1.1 301 Moved Permanently Location: http://stevesouders.com/newuri Content-Type: text/html
The browser automatically takes the user to the URL specified in theLocationfield. All the information necessary for a redirect is in the headers. The body of the response is typically empty. Despite their names, neither a 301 nor a 302 response is cached in practice unless additional headers, such asExpiresorCache-Control, indicate that it should be.There are other ways to automatically redirect users to a different URL. TheEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Types of Redirects
- InhaltsvorschauWhen web servers return a redirect to the browser, the response has a status code in the 3xx range. This indicates that further action is required of the user agent in order to fulfill the request. There are several 3xx status codes:
- 300 Multiple Choices (based on Content-Type)
- 301 Moved Permanently
- 302 Moved Temporarily (a.k.a. Found)
- 303 See Other (clarification of 302)
- 304 Not Modified
- 305 Use Proxy
- 306 (no longer used)
- 307 Temporary Redirect (clarification of 302)
"304 Not Modified" is not really a redirect—it's used in response to conditional GET requests to avoid downloading data that is already cached by the browser, as explained in . Status code 306 is deprecated.The 301 and 302 status codes are the ones used most often. Status codes 303 and 307 were added in the HTTP/1.1 specification to clarify the (mis)use of 302, but the adoption of 303 and 307 is almost nonexistent, as most web sites continue to use 302. Here's an example of the headers in a 301 response.HTTP/1.1 301 Moved Permanently Location: http://stevesouders.com/newuri Content-Type: text/html
The browser automatically takes the user to the URL specified in theLocationfield. All the information necessary for a redirect is in the headers. The body of the response is typically empty. Despite their names, neither a 301 nor a 302 response is cached in practice unless additional headers, such asExpiresorCache-Control, indicate that it should be.There are other ways to automatically redirect users to a different URL. Themeta refreshtag included in the head of an HTML document redirects the user after the number of seconds specified in thecontentattribute:<meta http-equiv="refresh" content="0; url=http://stevesouders.com/newuri">
JavaScript is also used to perform redirects by setting thedocument.locationto the desired URL. If you must do a redirect, the preferred technique is to use the standard 3xx HTTP status codes, primarily to ensure the Back button works correctly. For more information about this, see the W3C web article "Use standard redirects: don't break the back button!" at http://www.w3.org/qa/tips/rebackEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - How Redirects Hurt Performance
- Inhaltsvorschaushows how redirects slow down the user experience. The first HTTP request is the redirect. Nothing is displayed to the user until the redirect is completed and the HTML document is downloaded.
Figure : Redirects slow down web pagesIn , I talk about the importance of downloading stylesheets quickly; otherwise, the page is blocked from rendering. Similarly, explains how external scripts block the page from rendering and inhibit parallel downloads. The delays caused by redirects are even worse because they delay the delivery of the entire HTML document. Nothing in the page can be rendered and no components can be downloaded until the HTML document has arrived. Inserting a redirect between the user and the HTML document delays everything in the page.Redirects are typically used with requests for the HTML document, but occasionally you'll see them used for components in the page. shows the HTTP requests for Google Toolbar. It contains four redirects.
Figure : Multiple redirects including an imageThe sequence of requests and redirects is complex, so I'll walk through them one at a time:- The initial URL http://toolbar.google.com is requested.
- A 302 response is received with a
Locationof http://toolbar.google.com/t4. - http://toolbar.google.com/t4/ is requested.
- This HTML document redirects the user to http://www.google.com/tools/firefox/toolbar/index.html using JavaScript.
- The JavaScript redirect results in a 302 response with the
Locationhttp://www.google.com/tools/firefox/toolbar/ft3/intl/en/index.html. At this point there have been a total of three redirects—one done with JavaScript and two using the 302 status code. - Six images are downloaded.
- A seventh image is requested at http://toolbar.google.com/t3/intl/search.gif.
- This seventh image request results in a 302 response with the
Locationhttp://toolbar.google.com/intl/search.gif. - The final three images are requested.
The first four HTTP requests (redirect, HTML, script, and redirect) are used to get the user to the desired HTML document. These redirects account for more than half of the end user response time.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Alternatives to Redirects
- InhaltsvorschauRedirects are an easy way to solve many problems, but it's better to use alternative solutions that don't slow down page loading. The following sections discuss some of the typical situations in which redirects are used, and alternatives that are better for your users.One of the most wasteful redirects happens frequently and web developers are generally not aware of it. It occurs when a trailing slash (/) is missing from a URL that should otherwise have one. For example, the redirect illustrated in was generated by going to http://astrology.yahoo.com/astrology. This request results in a 301 response containing a redirect to http://astrology.yahoo.com/astrology/. The only difference is the addition of a trailing slash.There are good reasons for sending a redirect when the trailing slash is missing: it allows autoindexing (going to the index.html by default) and makes it possible to retrieve URLs in the page that are relative to the current directory (e.g., logo.gif). However, many popular web pages don't rely on autoindexing, instead relying on specific URLs and handlers. Additionally, URLs are often relative to the root and not to the current directory.Note that a redirect does not happen if the trailing slash is missing after the hostname. For example, http://www.yahoo.com does not generate a redirect. However, the resultant URL seen in your browser does contain the trailing slash: http://www.yahoo.com/. The automatic appearance of the trailing slash is caused because the browser must specify some path when it makes the GET request. If there is no path, as in http://www.yahoo.com, then it uses simply the document root (/):
GET / HTTP/1.1
Sending a redirect when a trailing slash is missing is the default behavior for many web servers, including Apache. TheAliasdirective is an easy workaround. Another alternative is to use themod_rewritemodule, butAliasis simpler. The problem with the Astrology site could be resolved by adding the following to the Apache configuration:Alias /astrology /usr/local/apache/htdocs/astrology/index.html
If you're using handlers in Apache 2.0, a cleaner solution is available in the form of theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 14: Rule 12: Remove Duplicate Scripts
- InhaltsvorschauIt hurts performance to include the same JavaScript file twice in one page. This mistake isn't as unusual as you might think. A review of the 10 top U.S. web sites shows that two of them (CNN and YouTube) contain a duplicated script.How does this happen? How does it affect performance? How can it be avoided? Let's take a look.Two main factors increase the odds of a script being duplicated in a single web page: team size and number of scripts.It takes a significant amount of resources to develop a web site, especially if it's a top destination. In addition to the core team building the site, other teams contribute to the HTML in the page for things such as advertising, branding (logos, headers, footers, etc.), and data feeds (news stories, sports scores, TV listings, etc.). With so many people from different teams adding HTML to the page, it's easy to imagine how the same script could be added twice. For example, two developers might be contributing JavaScript code that requires manipulating cookies, so each of them includes the company's cookies.js script. Both developers are unaware that the other has already added the script to the page.As shown in , the average number of scripts in the 10 top U.S. sites is greater than six (this information is also given in from ). The two sites that have duplicate scripts also happen to have an above-average number of scripts (CNN has 11; YouTube has 7). The more scripts in the page, the more likely it is that one of the scripts will be included twice.
Table : Number of scripts and stylesheets for 10 top sites Web siteScriptsStylesheets3118111272119122314173There are two ways that duplicate scripts hurt performance: unnecessary HTTP requests and wasted JavaScript execution.Unnecessary HTTP requests happen in Internet Explorer, but not in Firefox. In Internet Explorer, if an external script is included twice and is not cacheable, the browser generates two HTTP requests during page loading. This is demonstrated in the "Duplicate Scripts—Not Cached" example.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Duplicate Scripts—They Happen
- InhaltsvorschauTwo main factors increase the odds of a script being duplicated in a single web page: team size and number of scripts.It takes a significant amount of resources to develop a web site, especially if it's a top destination. In addition to the core team building the site, other teams contribute to the HTML in the page for things such as advertising, branding (logos, headers, footers, etc.), and data feeds (news stories, sports scores, TV listings, etc.). With so many people from different teams adding HTML to the page, it's easy to imagine how the same script could be added twice. For example, two developers might be contributing JavaScript code that requires manipulating cookies, so each of them includes the company's cookies.js script. Both developers are unaware that the other has already added the script to the page.As shown in , the average number of scripts in the 10 top U.S. sites is greater than six (this information is also given in from ). The two sites that have duplicate scripts also happen to have an above-average number of scripts (CNN has 11; YouTube has 7). The more scripts in the page, the more likely it is that one of the scripts will be included twice.
Table : Number of scripts and stylesheets for 10 top sites Web siteScriptsStylesheets3118111272119122314173Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Duplicate Scripts Hurt Performance
- InhaltsvorschauThere are two ways that duplicate scripts hurt performance: unnecessary HTTP requests and wasted JavaScript execution.Unnecessary HTTP requests happen in Internet Explorer, but not in Firefox. In Internet Explorer, if an external script is included twice and is not cacheable, the browser generates two HTTP requests during page loading. This is demonstrated in the "Duplicate Scripts—Not Cached" example.
- Duplicate Scripts—Not Cached
This won't be an issue for people who follow the advice in and add a far futureExpiresheader to their scripts, but if they don't, and they make the mistake of including the script twice, the user has to endure an extra HTTP request. explains how downloading scripts has an especially negative impact on response times. Subjecting the user to an extra HTTP request for a script doubles that negative impact.Even if the script is cacheable, extra HTTP requests occur when the user reloads the page. The following example includes scripts that are cacheable.- Duplicate Scripts—Cached
Load this page once to fill the cache, and then click the "Example 2 - Duplicate Scripts - Cached" link to load it again. Since the script is cached, no HTTP requests are made for the script, but if you click the browser's Refresh button, two HTTP requests are made. Specifically, two conditional GET requests are made. For more information, see the section "" in .In addition to generating unnecessary HTTP requests in Internet Explorer, time is wasted evaluating the script multiple times. This redundant JavaScript execution happens in both Firefox and Internet Explorer, regardless of whether the script is cacheable. In the previous examples, the duplicated script increments a counter that is displayed in the page. Because the script is included twice and evaluated twice, the value of the counter is 2.The problem of superfluous downloads and evaluations occurs for each additional instance of a script in the page. In the following example, the same script is included 10 times, which results in 10 evaluations. When you reload the page, 10 HTTP requests are made (only in Internet Explorer).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Avoiding Duplicate Scripts
- InhaltsvorschauOne way to avoid accidentally including the same script twice is to implement a script management module in your templating system. The typical way to include a script is to use the
SCRIPTtag in your HTML page:<script type="text/javascript" src="menu_1.0.17.js"></script>
An alternative in PHP would be to create a function calledinsertScript:<?php insertScript("menu.js") ?>While we're tackling the duplicate script issue, we'll add functionality to handle dependencies and versioning of scripts. A simple implementation ofinsertScriptfollows:<?php function insertScript($jsfile) { if ( alreadyInserted($jsfile) ) { return; } pushInserted($jsfile); if ( hasDependencies($jsfile) ) { $dependencies = getDependencies($jsfile); Foreach ($dependencies as $script) { insertScript($script); } } echo '<script type="text/javascript" src="' . getVersion($jsfile) . '"></script>"; } ?>The first time a script is inserted, we'll reachpushInserted. This adds the script to thealreadyInsertedlist for the page. If that script is accidentally inserted again, the test foralreadyInsertedwill not add the script again, thus solving the duplicate script issue.If this script has dependencies, those prerequisite scripts are inserted. In this example, menu.js might depend on events.js and utils.js. You can capture these dependency relationships using a hash or database. For simpler sites, dependencies can be maintained manually. For more complex sites, you may choose to automate the generation of dependencies by scanning the scripts to find symbol definitions.Finally, the script is echoed to the page. A key function here isgetVersion. This function looks up the script (in this case menu.js) and returns the filename with the appropriate version appended (e.g., menu_1.0.17.js). In , I mention the advantage of adding a version number to a component's filename; when using a far futureExpiresheader the filename has to be changed whenever the file contents are changed (see the section "" in ). Centralizing this functionality insideEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 15: Rule 13: Configure ETags
- InhaltsvorschauEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- What's an ETag?
- InhaltsvorschauEntity tags (ETags) are a mechanism that web servers and browsers use to validate cached components. Before jumping into the details of ETags, let's review how components are cached and validated.As the browser downloads components, it stores them in its cache. On subsequent page views, if the cached component is "fresh," the browser reads it from disk and avoids making an HTTP request. A component is fresh if it hasn't expired, based on the value in the
Expiresheader. Let's look at an example.When a component is requested, the server of origin has the option to send anExpiresheader back in the response:Expires: Thu, 15 Apr 2010 20:00:00 GMT
recommends setting an expiration date in the far future. How far is "far" depends on the component in question. An ad image might have to expire daily, whereas a company logo could expire in 10 years. The HTTP specification (http://www.w3.org/protocols/rfc2616/rfc2616-sec14.html#sec14.21) suggests servers should not set anExpiresdate more than one year in the future, but this is a guideline; browsers supportExpiresdates further in the future than one year. It's most efficient to avoid HTTP requests by setting the expiration date so far in the future that the components are unlikely to expire.In the event a cached component does expire (or the user explicitly reloads the page), the browser can't reuse it without first checking that it is still valid. This is called a conditional GET request (see the section "" in ). It's unfortunate that the browser has to make this HTTP request to perform a validity check, but it's more efficient than simply downloading every component that has expired. If the component in the browser's cache is valid (i.e., it matches what's on the origin server), instead of returning the entire component, the origin server returns a "304 Not Modified" status code.There are two ways in which the server determines whether the cached component matches the one on the origin server:- By comparing the last-modified date
- By comparing the entity tag
The component's last-modified date is returned by the origin server via theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Problem with ETags
- InhaltsvorschauThe problem with ETags is that they are typically constructed using attributes that make them unique to a specific server hosting a site. ETags won't match when a browser gets the original component from one server and later makes a conditional GET request that goes to a different server—a situation that is all too common on web sites that use a cluster of servers to handle requests. By default, both Apache and IIS embed data in the ETag that dramatically reduces the odds of the validity test succeeding on web sites with multiple servers.The ETag format for Apache 1.3 and 2.x is
inode-size-timestamp. Inodes are used by filesystems to store information such as file type, owner, group, and access mode. Although a given file may reside in the same directory across multiple servers and have the same file size, permissions, timestamp, etc., its inode is different from one server to the next.IIS 5.0 and 6.0 have a similar issue with ETags. The format for ETags on IIS isFiletimestamp:ChangeNumber.ChangeNumberis a counter used to track configuration changes to IIS. It's unlikely that theChangeNumberis the same across all IIS servers behind a web site.The end result is that ETags generated by Apache and IIS for the exact same component won't match from one server to another. If the ETags don't match, the user doesn't receive the small, fast 304 response that ETags were designed for; instead, they'll get a normal 200 response along with all the data for the component. If you host your web site on just one server, this isn't a problem, but if you use a cluster of servers, components have to be downloaded much more often than is required, which degrades performance.The unnecessary reloading of components also has a performance impact on your servers and increases your bandwidth costs. If you have n servers in your cluster in round-robin rotation, the probability that the ETag in the user's cache will match the server they land on next is 1/n. If you have 10 servers, the user has a 10% chance of getting the correct 304 response, leaving a 90% chance of getting a wasteful 200 response and full data download.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - ETags: Use 'Em or Lose 'Em
- InhaltsvorschauIf you have multiple servers hosting your web site and you're using Apache or IIS with the default ETag configuration, your users are getting slower pages, your servers have a higher load, you're consuming greater bandwidth, and proxies aren't caching your content efficiently. "But wait!" you say, "I followed Rule 3 and added a far future
Expiresheader to my components. There shouldn't be any conditional GET requests."Even if your components have a far futureExpiresheader, a conditional GET request is still made whenever the user clicks Reload or Refresh. There's no way around it—the problem with ETags has to be addressed.One option is to configure your ETags to take advantage of their flexible validation capabilities. One example might be a script that varies depending on whether the browser is Internet Explorer. Using PHP to generate the script, you could set theETagheader to reflect the browser state:<?php if ( strpos($_SERVER["HTTP_USER_AGENT"], "MSIE") ) { header("ETag: MSIE"); } else { header("ETag: notMSIE"); } ?>If you have components that have to be validated based on something other than the last-modified date, ETags are a powerful way of doing that.If you don't have the need to customize ETags, it is best to simply remove them. Both Apache and IIS have identified ETags as a performance issue, and suggest changing the contents of the Etag (see http://www.apacheweek.com/issues/02-01-18, http://support.microsoft.com/?id=922733, and http://support.microsoft.com/kb/922703 for more details).Apache versions 1.3.23 and later support theFileETagdirective. With this directive, theinodevalue can be removed from the ETag, leaving size and timestamp as the remaining components of the ETag. Similarly, in IIS you can set theChangeNumberto be identical across all servers, leaving the file timestamp as the only other piece of information in the ETag.Following these suggestions leaves an ETag that contains just the size and timestamp (Apache) or just the timestamp (IIS). However, because this is basically duplicate information, it's better to just remove the ETag altogether—theEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - ETags in the Real World
- Inhaltsvorschaushows that 6 out of 10 top U.S. web sites use ETags on a majority of their components. To be fair, three of them have modified the ETag format to remove
inode(Apache) orChangeNumber(IIS). Four or more contain ETags that haven't been modified and therefore cause the performance problems discussed previously.Table : ETags observed across 10 top web sites Web SiteComponents with ETagsFixed0% (0/24)n/a5% (3/63)yes83% (157/190)no86% (57/66)no0% (0/5)n/a72% (42/58)no84% (32/38)yes and no94% (16/17)unknown0% (0/34)n/a70% (43/61)yesAn example of a component with different ETags across the cluster of servers is http://stc.msn.com/br/hp/en-us/css/15/blu.css from http://msn.com. The HTTP headers from the first request in the example contains an ETag value of80b31d5a4776c71:6e0.GET /br/hp/en-us/css/15/blu.css HTTP/1.1 Host: stc.msn.com HTTP/1.1 200 OK Last-Modified: Tue, 03 Apr 2007 23:25:23 GMT ETag: "80b31d5a4776c71:6e0" Content-Length: 647 Server: Microsoft-IIS/6.0
On the first reload, the ETag matches and a 304 response is sent. TheContent-Lengthheader is missing from the response because the 304 status code tells the browser to use the content from its cache.GET /br/hp/en-us/css/15/blu.css HTTP/1.1 Host: stc.msn.com If-Modified-Since: Tue, 03 Apr 2007 23:25:23 GMT If-None-Match: "80b31d5a4776c71:6e0"
HTTP/1.1 304 Not Modified ETag: "80b31d5a4776c71:6e0" Last-Modified: Tue, 03 Apr 2007 23:25:23 GMT
On the second reload, the ETag changes to80b31d5a4776c71:47b. Instead of a fast 304 response with no content, a larger 200 response with the full content is returned.GET /br/hp/en-us/css/15/blu.css HTTP/1.1 Host: stc.msn.com If-Modified-Since: Tue, 03 Apr 2007 23:25:23 GMT If-None-Match: "80b31d5a4776c71:6e0"
HTTP/1.1 200 OK Last-Modified: Tue, 03 Apr 2007 23:25:23 GMT ETag: "80b31d5a4776c71:47b" Content-Length: 647 Server: Microsoft-IIS/6.0
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 16: Rule 14: Make Ajax Cacheable
- InhaltsvorschauPeople frequently ask whether the performance rules in this book apply to Web 2.0 applications. They definitely do! The rule discussed in this chapter is, however, the first rule that resulted from working with Web 2.0 applications at Yahoo!. In this chapter, I describe what Web 2.0 means, how Ajax fits into Web 2.0, and an important performance improvement you can make to Ajax.The relationship between Web 2.0, DHTML, and Ajax is illustrated in This figure doesn't show that Ajax is used only in DHTML or that DHTML is used only by Web 2.0 applications, but rather it is meant to show that Web 2.0 includes many concepts, one of which is DHTML, and that Ajax is one of the key technologies in DHTML. A discussion of Web 2.0 and what it includes is a book (or more) by itself, but we do want to have a common understanding of these terms. Below, I give brief definitions with references for more information.
Figure : Relationship between Web 2.0, DHTML, and AjaxO'Reilly Media first used the term "Web 2.0" in 2004, the first year they held the Web 2.0 conference. The term pertains not as much to technology as the social and business aspects of how the next-generation Internet will be used. It alludes to web sites evolving into Internet communities rich in wikis, blogs, and podcasts. Key concepts of Web 2.0 include a rich, application-like user interface and the aggregation of information from multiple web services. In effect, the web page becomes more and more like an application with well-defined inputs and outputs. DHTML and Ajax are technologies for implementing these concepts. In his web article "Web 2.0 Compact Definition: Trying Again" (http://radar.oreilly.com/archives/2006/12/web_20_compact.html), Tim O'Reilly provides one of the most referenced definitions of Web 2.0.Dynamic HTML allows the presentation of an HTML page to change after the page has loaded. This is accomplished using JavaScript and CSS interacting with the Document Object Model (DOM) in the browser. Examples include links that change when the user hovers the mouse over them, tree controls that expand and collapse, and cascading menus within the page. More complex DHTML pages may redraw the entire page based on the user's intention; for example, changing from viewing an email inbox to a form for composing an email message. Ajax is a technology used in DHTML so that the client can retrieve and display new information requested by the user without reloading the page.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Web 2.0, DHTML, and Ajax
- InhaltsvorschauThe relationship between Web 2.0, DHTML, and Ajax is illustrated in This figure doesn't show that Ajax is used only in DHTML or that DHTML is used only by Web 2.0 applications, but rather it is meant to show that Web 2.0 includes many concepts, one of which is DHTML, and that Ajax is one of the key technologies in DHTML. A discussion of Web 2.0 and what it includes is a book (or more) by itself, but we do want to have a common understanding of these terms. Below, I give brief definitions with references for more information.
Figure : Relationship between Web 2.0, DHTML, and AjaxO'Reilly Media first used the term "Web 2.0" in 2004, the first year they held the Web 2.0 conference. The term pertains not as much to technology as the social and business aspects of how the next-generation Internet will be used. It alludes to web sites evolving into Internet communities rich in wikis, blogs, and podcasts. Key concepts of Web 2.0 include a rich, application-like user interface and the aggregation of information from multiple web services. In effect, the web page becomes more and more like an application with well-defined inputs and outputs. DHTML and Ajax are technologies for implementing these concepts. In his web article "Web 2.0 Compact Definition: Trying Again" (http://radar.oreilly.com/archives/2006/12/web_20_compact.html), Tim O'Reilly provides one of the most referenced definitions of Web 2.0.Dynamic HTML allows the presentation of an HTML page to change after the page has loaded. This is accomplished using JavaScript and CSS interacting with the Document Object Model (DOM) in the browser. Examples include links that change when the user hovers the mouse over them, tree controls that expand and collapse, and cascading menus within the page. More complex DHTML pages may redraw the entire page based on the user's intention; for example, changing from viewing an email inbox to a form for composing an email message. Ajax is a technology used in DHTML so that the client can retrieve and display new information requested by the user without reloading the page.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Asynchronous = Instantaneous?
- InhaltsvorschauOne 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. In the article referenced earlier, Jesse James Garrett uses Google Suggest and Google Maps as examples of web interfaces where "everything happens almost instantly."Be careful! Using Ajax is no guarantee that the user won't be twiddling his thumbs waiting for those "asynchronous JavaScript and XML" responses to return. I'd hate to use Google Maps and Yahoo! Maps on a dial-up connection. In many applications, whether or not the user is kept waiting depends on how Ajax is used. Frontend engineers once again shoulder the responsibility of identifying and following the best practices required to ensure a fast user experience.A key factor to whether the user might be kept waiting is whether the Ajax requests are passive or active. Passive requests are made in anticipation of a future need. For example, in a web-based email client, a passive request might be used to download the user's address book before it's actually needed. By loading it passively, the client makes sure the address book is already in its cache when the user needs to address an email message. Active requests are made based on the user's current actions. An example is finding all the email messages that match the user's search criteria.The latter example illustrates that even though active Ajax requests are asynchronous, the user may still be kept waiting for the response. It is true that, thanks to Ajax, the user won't have to endure a complete page reload, and the UI is still responsive while the user waits. Nevertheless, the user is most likely sitting, waiting for the search results to be displayed before taking any further action. It's important to remember that "asynchronous" does not imply "instantaneous." I definitely agree with Jesse James Garrett's final FAQ.Q:Do Ajax applications always deliver a better experience than traditional web applications?A:Not necessarily. Ajax gives interaction designers more flexibility. However, the more power we have, the more caution we must use in exercising it. We must be careful to use Ajax to enhance the user experience of our applications, not degrade it.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Optimizing Ajax Requests
- InhaltsvorschauThe previous section makes it clear that it's possible that the user will be kept waiting when making active Ajax requests. To improve performance, it's important to optimize these requests. The techniques for optimizing active Ajax requests are equally applicable to passive Ajax requests, but since active requests have a greater impact on the user experience, you should start with them.To find all the active Ajax requests in your web application, start your favorite packet sniffer. (The section "" in mentions my favorite packet sniffer: IBM Page Detailer.) After your web application has loaded, start using it while watching for Ajax requests that show up in the packet sniffer. These are the active Ajax requests that have to be optimized for better performance.The most important way to improve these active Ajax requests is to make the responses cacheable, as discussed in . Some of the other 13 rules we've already covered are also applicable to Ajax requests:
- Rule 4: Gzip Components
- Rule 9: Reduce DNS Lookups
- Rule 10: Minify JavaScript
- Rule 11: Avoid Redirects
- Rule 13: ETags—Use 'Em or Lose 'Em
However, Rule 3 is the most important. It might not be fair for me to create a new rule that simply reapplies previous rules in a new context, but I've found that, because Ajax is so new and different, these performance improvements have to be called out explicitly.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Caching Ajax in the Real World
- InhaltsvorschauLet's take a look at some examples to see how Ajax adheres to these performance guidelines in the real world.In our first example, we'll look at the Ajax version of Yahoo! Mail (http://mail.yahoo.com), which is in beta at the time of this writing.When the user starts the Ajax version of Yahoo! Mail, it downloads the body of the user's first three email messages. This is a smart passive Ajax request. There's a good chance the user will click on one or more of these email messages, so having them already downloaded in the client means that the user sees her email messages without having to wait for any Ajax responses.If the user wants to view an email message that's not in the first three, an active Ajax request is made. The user is waiting for this response so she can read the email message. Let's look at the HTTP headers.
GET /ws/mail/v1/formrpc?m=GetMessage[snip...] HTTP/1.1 Host: us.mg0.mail.yahoo.com Accept-Encoding: gzip,deflate
HTTP/1.1 200 OK Date: Mon, 23 Apr 2007 23:22:57 GMT Cache-Control: no-store, private Expires: Thu, 01 Jan 1970 00:00:00 GMT Content-Type: text/xml; charset=UTF-8 Content-Encoding: gzip Connection: keep-alive
Now imagine that the user leaves Yahoo! Mail to visit another web site. Later, she returns to Yahoo! Mail and again clicks on the fourth email message. Not surprisingly, the exact same request is sent again because the previous Ajax response was not saved in the browser's cache. It wasn't cached because the response contains aCache-Controlheader with the valueno-store, and anExpiresheader with a date in the past. Both of these tell the browser not to cache the response. And yet, if her inbox hasn't changed, the content is identical in both responses.If these headers were replaced with a far futureExpiresheader (see ) the response would be cached and read off disk, resulting in a faster user experience. This might seem counterintuitive to some developers—after all, this is a dynamically generated response that contains information relevant to only one user in the world. It doesn't seem to make sense to cache this data. The critical thing to remember is that this one user might make that request multiple times in a day or week. If you can make the response cacheable for her, it may make the difference between a slow user experience and a fast one.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 17: Deconstructing 10 Top Sites
- InhaltsvorschauWhat follows is an examination of 10 top U.S. web sites using the rules and tools described in this book. This analysis gives examples of how to identify performance improvements in real-world web sites. My hope is that after walking through these examples you will look at web sites with the critical eye of a performance advocate.shows the page weight, response time, and YSlow grade for the home pages of 10 top U.S. web sites as measured in early 2007. YSlow is a performance tool developed at Yahoo! that produces a single score (A is best, F is worst) for how well a page satisfies the performance rules described in this book. See the upcoming section "" for more information.
Table : Performance summary of 10 top U.S. web sites Page weightResponse timeYSlow gradeAmazon405K15.9 secDAOL182K11.5 secFCNN502K22.4 secFeBay275K9.6 secCGoogle18K1.7 secAMSN221K9.3 secFMySpace205K7.8 secDWikipedia106K6.2 secCYahoo!178K5.9 secAYouTube139K9.6 secDNot surprisingly, page weight and response time are strongly correlated, with a correlation coefficient of 0.94, as shown in . This makes sense—adding more components or larger components to the page makes it slower. Plotting page weight and response time throughout the development process is a worthwhile analysis for any web page undergoing performance improvements.
Figure : Page weight and response time are correlatedYSlow grades are a strong indicator of the response time of a page, as shown in . A high (good) YSlow grade indicates a well-built page that is fast and lean. A page with a low (bad) YSlow grade is probably going to be slow and heavier. Since the YSlow grade is inversely related to response time and page weight, the inverse YSlow grade is plotted in . YSlow grades are typically indicated as A, B, C, D, or F, but behind the letter grade is a numeric score on the scale 0–100.Yahoo! doesn't quite follow the curve. It has the second-best YSlow grade (its score is A, at 95, which is slightly lower than Google's A, a perfect 100) and response time, even though it's the fourth-heaviest page. The Yahoo! home page team is a long-time consumer of these performance best practices, and therefore scores well in YSlow and is able to squeeze more speed out of their page. Amazon's YSlow grade also doesn't reflect the page weight and response time. The main reason for this is the large number of images in their page (approximately 74 images). YSlow doesn't subtract points for images, so the Amazon page scores well, but performs slowly.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Page Weight, Response Time, YSlow Grade
- Inhaltsvorschaushows the page weight, response time, and YSlow grade for the home pages of 10 top U.S. web sites as measured in early 2007. YSlow is a performance tool developed at Yahoo! that produces a single score (A is best, F is worst) for how well a page satisfies the performance rules described in this book. See the upcoming section "" for more information.
Table : Performance summary of 10 top U.S. web sites Page weightResponse timeYSlow gradeAmazon405K15.9 secDAOL182K11.5 secFCNN502K22.4 secFeBay275K9.6 secCGoogle18K1.7 secAMSN221K9.3 secFMySpace205K7.8 secDWikipedia106K6.2 secCYahoo!178K5.9 secAYouTube139K9.6 secDNot surprisingly, page weight and response time are strongly correlated, with a correlation coefficient of 0.94, as shown in . This makes sense—adding more components or larger components to the page makes it slower. Plotting page weight and response time throughout the development process is a worthwhile analysis for any web page undergoing performance improvements.
Figure : Page weight and response time are correlatedYSlow grades are a strong indicator of the response time of a page, as shown in . A high (good) YSlow grade indicates a well-built page that is fast and lean. A page with a low (bad) YSlow grade is probably going to be slow and heavier. Since the YSlow grade is inversely related to response time and page weight, the inverse YSlow grade is plotted in . YSlow grades are typically indicated as A, B, C, D, or F, but behind the letter grade is a numeric score on the scale 0–100.Yahoo! doesn't quite follow the curve. It has the second-best YSlow grade (its score is A, at 95, which is slightly lower than Google's A, a perfect 100) and response time, even though it's the fourth-heaviest page. The Yahoo! home page team is a long-time consumer of these performance best practices, and therefore scores well in YSlow and is able to squeeze more speed out of their page. Amazon's YSlow grade also doesn't reflect the page weight and response time. The main reason for this is the large number of images in their page (approximately 74 images). YSlow doesn't subtract points for images, so the Amazon page scores well, but performs slowly.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - How the Tests Were Done
- InhaltsvorschauReviewing these 10 top web sites illustrates how performance best practices are followed in real world pages. A problem in doing an analysis of this type is that the subject of the analysis is a moving target—these web sites are constantly changing. For example, during my analysis one web site switched from IIS to Apache. It's possible, and likely, that the page I analyzed is not the one you'll see if you visit that web site today. Ideally, the page you find will implement the suggestions and other best practices highlighted here, and will perform well and load quickly.The charts of HTTP requests were generated by IBM Page Detailer (http://alphaworks.ibm.com/tech/pagedetailer). This is my preferred packet sniffer. It works across all HTTP clients. I like the way IBM Page Detailer indicates how the HTTP requests are associated to the corresponding HTML document. The HTTP chart makes it easy to identify bottlenecks in component downloads. The bars are color-coded to indicate the type of component being downloaded.The response times were measured using Gomez's web monitoring services (http://www.gomez.com). The response time is defined as the time from when the request is initiated to when the page's
onloadevent fires. Each URL was measured thousands of times over low broadband (56K-512K); the average value is what is shown here.I used Firebug (http://www.getfirebug.com) to analyze the JavaScript and CSS in the various pages. Firebug is a critical tool for any frontend engineer. Its strongest feature is the ability to debug JavaScript code, but that's just a fraction of what it can do. Firebug also provides functionality to inspect the DOM, tweak CSS, execute JavaScript, and explore the page's HTML.The main tool used to analyze the performance of these pages was YSlow (http://developer.yahoo.com/yslow). I built YSlow for Yahoo! development teams to help them identify the changes that could lead to the greatest improvements in performance. Joe Hewitt, Firebug's author, provided support for the integration of YSlow with Firebug. This is an ideal combination since frontend engineers already use Firebug during development.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Amazon
- Inhaltsvorschau
Figure : http://www.amazon.comAmazon (http://www.amazon.com) is a relatively heavy page with a total page weight of 405K and 84 HTTP requests. Given the size and number of components in the page, the biggest performance improvement for Amazon would be to add a far futureExpiresheader to their components (Rule 3). Only 3 out of 84 components have anExpiresheader. They use only one stylesheet and three scripts. The scripts are loaded one after the other, so it would be a simple improvement to combine them into a single HTTP request. The stylesheet and scripts should be gzipped. The three scripts are minified to a large degree, but further savings could be gained by removing all comments and extraneous carriage returns.Even with the performance improvements identified by YSlow, the sheer number of images in the page (74) is a challenge. Nineteen of these images are used as backgrounds in CSS rules. Converting them into CSS sprites would reduce the total HTTP requests from 84 to 66.Looking at the subset of HTTP requests shown in the waterfall chart in , we see that because these images are all requested from the same hostname, only two images are downloaded in parallel, increasing the total page load time. Splitting this large number of images across two hostnames would double the amount of parallel downloads, significantly reducing the end user response time. Two is the number of hostnames recommended in in the "" section.
Figure : Amazon HTTP requestsEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - AOL
- Inhaltsvorschau
Figure : http://www.aol.comThe HTTP requests for AOL (http://www.aol.com) show a high degree of parallelization of downloads in the first half, but in the second half, the HTTP requests are made sequentially (see ). In turn, the page load time is increased. There are two interesting implementation details here: downgrading to HTTP/1.0 and multiple scripts.
Figure : AOL HTTP requestsIn the first half, where there is greater parallelization, the responses have been downgraded from HTTP/1.1 to HTTP/1.0. I discovered this by looking at the HTTP headers where the request method specifiesHTTP/1.1, whereas the response statesHTTP/1.0.GET /_media/aolp_v21/bctrl.gif HTTP/1.1 Host: www.aolcdn.com
HTTP/1.0 200 OK
For HTTP/1.0, the specification recommends up to four parallel downloads per hostname, versus HTTP/1.1's guideline of two per hostname. Greater parallelization is achieved as a result of the web server downgrading the HTTP version in the response.Typically, I've seen this result from outdated server configurations, but it's also possible that it's done intentionally to increase the amount of parallel downloading. At Yahoo!, we tested this, but determined that HTTP/1.1 had better overall performance because it supports persistent connections by default (see the section "" in ).There are no parallel downloads in much of the second half of AOL's HTTP traffic because most of these requests are scripts. As described in , all other downloading is blocked while the browser downloads external scripts. This results in a small number of requests spreading out over a longer time period than if they were done in parallel.These scripts appear to be used for ads, but the insertion of the scripts seems inefficient. The scripts come in pairs. The first script contains:document.write('<script type="text/javascript" src="http://twx.doubleclick.net/adj/ TW.AOLCom/Site_WS[snip...]script>\n');This causes the second script to be downloaded from http://twx.doubleclick.net. It contains the content of the ad:document.write('<!-- Template Id = 4140 Template Name = AOL - Text - WS Portal ATF DR 2-line (291×30) -->\n<B>Free Credit Score</B>[snip...]');Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - CNN
- Inhaltsvorschau
Figure : http://www.cnn.comCNN (http://www.cnn.com) is the heaviest of the 10 top web sites in both total page weight (502K) and number of HTTP requests (198!). The main reason for this is the use of images to display text. For example, the image http://i.a.cnn.net/cnn/element/img/1.5/main/tabs/topstories.gif is the text "Top Stories," as shown in .
Figure : Text rendered in an imageOver 70 images contain only text. Capturing text as an image allows for a customized appearance that may not be possible with text fonts. The tradeoff, as seen in the download statistics, is an increase in page weight and HTTP requests, resulting in a slower user experience. Also, internationalization is more challenging, as each translation requires a new set of images. Rule 1 tells us that reducing the number of components is the most important step to faster performance. Replacing these images with text would yield the biggest performance improvement for this page.Similarly, there are 16 images used for CSS backgrounds. If these were combined into a few CSS sprites, as described in , 10 or more HTTP requests would be eliminated. Combining the 10 separate JavaScript files together would eliminate another 9 HTTP requests.Further, more than 140 of the components in the page do not have anExpiresheader and thus are not cached by the browser (Rule 3). None of the stylesheets or scripts is gzipped (Rule 4) and most of the scripts aren't minified (Rule 10). The stylesheets add up to 87K and the scripts are 114K, so gzipping and minifying would significantly reduce the total page weight. Over 180 of the components have the default ETag from Apache. As described in , this means that it's unlikely the more efficient 304 status code can be used when conditional GET requests are made. This is especially bad in this case because most components must be validated since they don't have a futureExpiresheader.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - eBay
- Inhaltsvorschau
Figure : http://www.ebay.comThe YSlow grade for eBay (http://www.ebay.com) is very close to a B. With a little bit of work it would perform well. The main problems are with Rules 1, 3, 9, and 13.- Rule 1: Make Fewer HTTP Requests
- The eBay page has 10 scripts and 3 stylesheets. A simple fix would be to use the combination technique described in . Four of the scripts are loaded close together at the top of the page, and three are at the bottom of the page. These should be combined into a top script and a footer script, reducing 10 scripts to 5. The three stylesheets are all loaded close together and should also be combined.
- Rule 3: Add an Expires Header
- One script and one stylesheet have an
Expiresheader that is only nine hours in the future. According to theLast-Modifieddate, the stylesheet hasn't been modified in 3 days, and the script in 24 days. These are likely assets that change over time, but given the number of users of the site, it would be better to use a far futureExpiresheader to make these files cacheable. Additionally, there are five IFrames without anExpiresheader. These are used to insert ad images, some of which don't have anExpiresheader as well. - Rule 9: Reduce DNS Lookups
- Nine different domains are used in the eBay page. Typically, a domain count this high includes several domains from third-party advertisers, but in this case, there are seven domains related to eBay, and only two used by third-party advertisers.
- Rule 13: ETags—Configure ETags
- Fifty-two components are served from IIS using the default ETag. As explained in , this causes components to be downloaded much more frequently than necessary. This is exacerbated by the fact that these components have an expiration date that is at most 45 days in the future. As the components become stale and the conditional GET request is made, the ETag is likely to spoil the chances of getting a fast "304 Not Modified" response, and instead end up sending back the entire component even though it already resides on the user's disk.
The use of IFrames to serve ads is worth discussing. IFrames achieve a clear separation between ads and the actual web page, allowing those teams and systems to work independently. The downside is that each IFrame is an additional HTTP request that typically (as in this case) is not cached.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Inhaltsvorschau
Figure : http://www.google.comGoogle is known for its simple and fast page design. Its home page, http://www.google.com, is just 18K in total page size and issues just 3 HTTP requests (the HTML document and 2 images). However, even in this simple page there are several performance optimizations worth noting.The Google page is just three HTTP requests, but shows five HTTP requests.
Figure : Google HTTP requestsThe two extra requests aren't really part of the page. One is http://www.google.com/favicon.ico (see ). Favicons are used to associate a visual image with a URL. They are displayed next to the URL at the top of the browser, next to each URL in the list of Bookmarks or Favorites, and in tabs (for tab-enabled browsers). Browsers fetch them the first time a web site is loaded. If a web site doesn't have a favicon, a default icon is used.
Figure : http://www.google.com/favicon.icoThe second extra request is for http://www.google.com/images/nav_logo3.png, shown in . This is a CSS sprite, a combination of images that was described in . I say it is not part of the page because it is loaded after the page is done, as part of theonloadevent in the Google home page:onload="sf();if(document.images){new Image( ).src='/images/nav_logo3.png'}"Thesf( )function call sets the input focus to the search field. The second statement creates an image object usingnew Image( ). The image object'ssrcattribute is set to/images/nav_logo3.png. This is a typical way to load images dynamically, except for one thing: the new image isn't assigned to a variable. There is no easy way for the page to access this image later. That's OK, though, because this page has no intention of using the image. The nav_logo3.png image is downloaded in anticipation of future pages the user is expected to visit. Notice how this CSS sprite has the next and previous arrows used to page through the search results. It also contains images used in other pages, such as a checkout button and shopping cart.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - MSN
- Inhaltsvorschau
Figure : http://www.msn.comThe MSN home page (http://www.msn.com), ranks in the middle among the sites examined in this chapter when it comes to total size and number of HTTP requests. It fails to meet some basic performance guidelines, due especially to the way ads are inserted. However, it has several positive performance traits not seen in any of the other web sites analyzed here. Let's start by looking at how MSN does ads, because this will come up in several of the following recommendations.MSN uses IFrames to insert five ads into the page. As discussed earlier, with regard to eBay, using IFrames is an easy way to remove dependencies between the ads system and the HTML page generation system. However, each IFrame results in an additional HTTP request. In the case of MSN, each IFrame'sSRCattribute is set toabout:blank, which doesn't generate any HTTP traffic. However, each IFrame contains an external script that inserts an ad into the page using JavaScript anddocument.write. Integrating the ad system and the HTML page generation system would preclude the need for these five HTTP requests. Instead of requesting a script that contains multipledocument.writestatements, that JavaScript could be inlined in the HTML document.- Rule 1: Make Fewer HTTP Requests
- The MSN home page has four scripts (other than the scripts used for ads), three of which are loaded very close together and could be combined. It also has over 10 CSS background images. These could be combined using CSS sprites.
- Rule 3: Add an Expires Header
- One script is not cacheable because its expiration date is set in the past. The five scripts used to insert ads also have an expiration date in the past, and so they aren't cacheable. It's likely the JavaScript couldn't be cached, but if the ads were inserted into the HTML page itself, these five external script files wouldn't be required.
- Rule 4: Gzip Components
- Two scripts and two stylesheets are not gzipped. Also, the five scripts used to serve ads are not gzipped.
- Rule 9: Reduce DNS Lookups
- Twelve domains are used in the MSN home page. This is more than most web pages, but we'll discuss later how this is a benefit in increasing parallel downloads.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - MySpace
- Inhaltsvorschau
Figure : http://www.myspace.comIt's a challenge for web sites geared toward user-generated content to achieve fast performance—the content is varied and changes frequently. Nevertheless, there are some simple changes that would improve the response time of MySpace (http://www.myspace.com).- Rule 1: Make Fewer HTTP Requests
- Combining scripts and stylesheets would reduce the number of HTTP requests. The page has six scripts, three of which are loaded close together at the top of the page and could easily be combined. The three stylesheets are also loaded close together at the top of the page, making it easy to combine them as well.
- Rule 3: Add an Expires Header
- The MySpace page has over a dozen images with no
Expiresheader. Some of the images in the page understandably wouldn't benefit from anExpiresheader because they rotate frequently, such as in the new videos and new people sections of the page. However, some of the images that are used on every page also do not have anExpiresheader. - Rule 9: Reduce DNS Lookups
- The impact of DNS lookups would be reduced by eliminating some of the 10 unique domains used in the page.
- Rule 10: Minify JavaScript
- Four scripts, totaling over 20K, are not minified.
As shown in , there's a high degree of parallelized HTTP requests in the middle of the page, but the beginning of the page is negatively affected by the blocking behavior of scripts and stylesheets (this blocking behavior is described in ). Combining these files would lessen the impact. The effect is worse here because the HTTP requests were measured in Firefox. In addition to scripts blocking parallel downloads (in both Firefox and Internet Explorer), stylesheets also block parallel downloads (only in Firefox). Nevertheless, combining scripts and doing the same for stylesheets would improve the performance for both Firefox and Internet Explorer.
Figure : MySpace HTTP requestsEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Wikipedia
- Inhaltsvorschau
Figure : http://www.wikipedia.orgThe Wikipedia page is relatively small and fast. It would be faster if the 10 images used as navigation icons at the bottom of the page were converted to a CSS sprite. Further, there are two stylesheets that should be combined. These simple improvements would reduce the page's HTTP requests from 16 to just 6: the HTML document, 1 stylesheet, 3 images, and 1 CSS sprite.None of the images has anExpiresheader. This is the second-most important performance improvement for Wikipedia. Some of the images haven't been modified in over a year. Adding a far futureExpiresheader would improve the response time for millions of users, without adding much burden to the development process when images change.Also, the stylesheets should be gzipped. They currently total about 22K, and gzipping them would reduce the number of bytes downloaded to 16K.Most of Wikipedia's images are in PNG format. The PNG format is frequently chosen over GIF because of its smaller file size, as well as greater color depth and transparency options. It's likely that using the PNG format saved Wikipedia several kilobytes of data to download (it's not possible to convert their PNG images to GIF for comparison because of the loss of color depth). However, even after choosing the PNG format, further optimization can bring the file sizes down even more. For example, optimizing Wikipedia's 12 PNG images brought the total size from 33K down to 28K, a 15% savings. There are several PNG optimizers available—I used PngOptimizer (http://psydk.org/pngoptimizer.php). Adding a PNG optimization step to their development process would improve Wikipedia's performance.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Yahoo!
- Inhaltsvorschau
Figure : http://www.yahoo.comYahoo! (http://www.yahoo.com) is the fourth-heaviest page in total bytes among the ones examined in this chapter, but second in response time and YSlow grade. The Yahoo! home page team has been engaged with my performance team for years, and is constantly tracking and working to improve response times. As a result, their YSlow scores are high, and they are able to squeeze more speed out of their page.Yahoo!'s home page has four CSS sprite images. It has been using sprites for years and was the first web site in which I encountered the use of sprites. One of these sprites is icons_1.5.gif. Looking at the list of components, we see that this image is downloaded twice. On further investigation, the issue is that two URLs reference the exact same image:http://us.js2.yimg.com/us.js.yimg.com/i/ww/sp/icons_1.5.gif http://us.i1.yimg.com/us.yimg.com/i/ww/sp/icons_1.5.gif
How does a mistake like this happen? Template variables are most likely used to build these URLs. The CSS rules that include this background image are both inlined in the HTML document, so presumably both had access to the same template variables. The us.js2.yimg.com hostname is used for all of the scripts, and us.i1.yimg.com is used solely for images and Flash. Most likely, the "JavaScript" hostname, us.js2.yimg.com, was accidentally used for this CSS background image.This look at the use of hostnames reveals some nice performance optimizations in the Yahoo! home page. They have split their components across multiple hostnames, resulting in an increase in simultaneous downloads, as shown in . Also, they have chosen the domain yimg.com, which is different from the page's hostname, yahoo.com. As a result, the HTTP requests to yimg.com will not be encumbered with any cookies that exist in the yahoo.com domain. When I'm logged in to my personal Yahoo! Account, my yahoo.com cookies are over 600 bytes, so this adds up to a savings of over 25K across all the HTTP requests in the page.
Figure : Yahoo! HTTP requestsThe names of two elements are intriguing:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - YouTube
- Inhaltsvorschau
Figure : http://www.youtube.comYouTube's home page (http://www.youtube.com) isn't very heavy, but it has a low YSlow grade and ends up in the bottom half of response times. shows that there isn't very much parallelization at the beginning and end. Increasing the level of parallelization in these areas would make the greatest improvement to response times.
Figure : YouTube HTTP requestsIn the beginning of the page load, the main hurdle to parallelization is the six scripts downloaded back-to-back. As explained in , scripts block all other downloads, no matter what their hostnames are. Additionally, the scripts aren't minified. Combining these six scripts into a single script and minifying them would decrease the download time. Also, if any of these scripts could be downloaded later in the page, the initial part of the page would be downloaded and rendered sooner.At the end of the page, decreased parallelization results from downloading 15 images from a single hostname (img.youtube.com). YouTube only uses four unique hostnames in their page. It would be worth the cost of an extra DNS lookup to split these 15 downloads across two hostnames and double the amount of simultaneous downloads.Sadly, not a single component has a far futureExpiresheader (Rule 3). Most of the components in the page are user-generated images that rotate frequently. Adding anExpiresheader to these might have little benefit, but the other components in the page don't change so often. Eleven of the components haven't changed in six months or more. Adding a far futureExpiresheader to these components would improve the response times for subsequent page views.YouTube uses the Apache web server, and their components still contain Etags, but YouTube has made the extra effort to modify the ETag syntax to improve their cacheability, as explained in .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
Zurück zu High Performance Web Sites
