You could also use Fiddler which will work for
browsers other than Firefox. (But will not profile javascript code)
HTTP Performance: An Overview
It's no secret—users love fast Web sites. Users are notoriously impatient,
and unless your Web site has no competitive substitute, users are unlikely to
stick around if your site's performance doesn't measure up. If your site has
visitors from around the world, ensuring your site operates efficiently is even
more critical, as international network connections generally suffer from the
twin banes of snappy sites: high latency and low bandwidth.
There are many options for improving your site's performance: compression,
caching, geographic load balancing, adding hardware, and so forth. Optimizing
the use of compression and caching is often the best place to start, as
configuration changes are generally free and can return dramatic benefits.
In this article, we'll use the Fiddler HTTP Debugger to explore HTTP
performance, caching, and compression.
Tweaking "First Visit" Performance
On their crucial first visit to your site, visitors must download every piece
of content used to generate the page, including JScript, CSS, images, and HTML.
If your page is too slow to load, visitors may leave your page before it's even
done downloading!
By exposing all HTTP traffic, Fiddler readily shows which files are used to
generate a given page. Shift+click multiple entries in the HTTP Sessions list
to calculate the "total page weight"—the number of requests and the
bytes transferred.
Figure 1. Fiddler's Performance Statistics View
The best way to ensure a "Wow, this is fast" first impression is
to deliver fewer and smaller files.
Tips for fast first-visits:
- Use fewer
graphics.
- Extract
styles into a single CSS file.
- Extract
script blocks into a single JS file.
- Simplify
your page layout.
- Use HTTP
Compression.
Once you've tuned your site for a fast first visit, you can make it even
faster for return visitors by taking advantage of HTTP caching.
Introduction to HTTP Caching
Two key factors in improving the speed of your Web applications are:
- Reducing
the number of request/response roundtrips.
- Reducing
the number of bytes transferred between the server and the client.
HTTP caching is of the best ways to reduce roundtrips and bytes transferred.
Caching provides a mechanism for a client or proxy to store HTTP responses for
later use, so that requests need not cross the network.
Other than performance, another benefit of maximizing use of HTTP caching
comes from the fact that bandwidth isn't free. By tuning caching for a major
Microsoft site, we were able to reduce our outbound bandwidth costs by over
$10,000
per month.
Cache-Related Request Headers
To enhance performance, Microsoft Internet Explorer and other Web clients
maintain a local cache of resources downloaded from remote Web servers.
When a resource is needed by the client, there are three possible actions:
- Send a
plain HTTP request to the remote Web server asking for a resource
- Send a conditional HTTP request to the
origin server asking for the resource only
if it differs from the locally cached version
- Use a
locally cached version of the resource, if a cached copy is available
When sending a request, the client may use one of the following headers:
Table 1. Client Cache Headers
|
Pragma:
no-cache
|
The client is unwilling to accept any cached responses
from caches along the route and the origin server must be contacted for a
fresh copy of the resource.
|
|
If-Modified-Since:
datetime
|
The server should return the requested resource only if
the resource has been modified since the date-time provided by the client.
|
|
If-None-Match:
etagvalue
|
The server should return the requested resource if the
ETAG of the resource is different than the value provided by the client. An ETAG
is a unique identifier representing a particular version of a file.
|
A client indicates that it has a cached response available for use by
sending a "Conditional request" containing the headers
If-Modified-Since
or
If-None-Match. If the server replies to a conditional
request with
HTTP/304 Not Modified, the client is directed to
reuse its cached response. Otherwise, the server should return a new response
and the client should discard its outdated cache item.
Observe two consecutive requests for an image file in the following code
sessions. In the first session, no locally cached version of the file is
present, so the server returns the file along with an
ETAG
value and the date-time of the last modification of the file. In the subsequent
session, a locally cached version of the file is now available, so a
conditional request is made, passing up the
ETAG of the cached
response as well as the
Last-Modified time of the original
request. Since the image has not changed since the cached version (either
because the
ETAG matches or the
If-Modified-Since
value matches the
Last-Modified value) the server returns a
304 to the client to direct it to use the cached response.
Session #1
GET /images/banner.jpg HTTP/1.1
Host: www.bayden.com
HTTP/1.1 200 OK
Date: Tue, 08 Mar 2006 00:32:46 GMT
Content-Length: 6171
Content-Type: image/jpeg
ETag: "40c7f76e8d30c31:2fe20"
Last-Modified: Thu, 12 Jun 2003 02:50:50 GMT
Session #2
GET /images/banner.jpg HTTP/1.1
If-Modified-Since: Thu, 12 Jun 2003 02:50:50 GMT
If-None-Match: "40c7f76e8d30c31:2fe20"
Host: www.bayden.com
HTTP/1.1 304 Not Modified
Because an
HTTP/304 response contains only headers and no
body, it crosses the network much more quickly than if the full resource had
been re-downloaded. However, even an
HTTP/304 requires a full
roundtrip to the remote Web server; by carefully setting response headers, a
Web application developer can eliminate the need to issue even conditional
requests.
Cache-Related Response Headers
Generally, the cacheability of an HTTP response is controlled by headers
sent in the response. The
HTTP
specification describes the headers that control caching. The optional
Cache-Control
and
Expires headers are the primary mechanisms for a Web
server to indicate to a proxy or a client how content may be cached.
The
Expires
header contains an absolute date-time after which a cached copy of a response
should no longer be considered
fresh. If the
Expires
header contains something other than a date (
0 or
-1
are common values), the response should immediately be treated as
stale.
A
fresh cache entry may be reused without contacting the server again;
a
stale cache entry should not be reused without first contacting the
Web server to ensure that it is still up-to-date.
For example, let's look at the previous example, except we'll add an
Expires
header to the first response:
Session #1
GET /images/banner.jpg HTTP/1.1
Host: www.bayden.com
HTTP/1.1 200 OK
Date: Tue, 08 Mar 2006 00:32:46 GMT
Content-Length: 6171
Content-Type: image/jpeg
Expires: Tue, 12 Jun 2007 02:50:50 GMT
Last-Modified: Thu, 12 Jun 2003 02:50:50 GMT
Session #2
<no HTTP request is made; cached version is used automatically>
As you can see, we've improved performance by adding an
Expires
header, since no conditional HTTP request is made during Session #2.
Similarly, the
Cache-Control header contains a list of
tokens that control caching. Any
Cache-Control
directives supersede the
Expires header.
Commonly used
Cache-Control tokens include those found in
table 2.
Table 2. Common Cache-Control Headers
|
Value
|
Meaning
|
|
public
|
The response may be stored in any cache, including caches
shared among many users.
|
|
private
|
The response may only be stored in a private cache used by
a single user.
|
|
no-cache
|
The response should not be reused to satisfy future
requests.
|
|
no-store
|
The response should not be reused to satisfy future
requests, and should not be written to disk. This is primarily used as a
security measure for sensitive responses.
|
|
max-age=#seconds
|
The response may be reused to satisfy future requests
within a certain number of seconds.
|
|
must-revalidate
|
The response may be reused to satisfy future requests, but
the origin server should first be contacted to verify that the response is
still fresh.
|
Using the HTTP Sessions list, Fiddler users can see whether pages contain
HTTP Caching headers.
Figure 2. Fiddler Sessions List
If a response does not contain
Expires or
Cache-Control
headers, the client may be forced to issue a conditional request to ensure that
the resource is still fresh.
Conditional Requests and the WinInet Cache
Internet Explorer takes advantage of the caching services provided by
Microsoft Windows Internet Services (WinInet).
WinInet allows the user to configure the size and behavior of the cache. To
access the cache settings:
- Open
Internet Explorer.
- On the Tools menu, choose Internet Options.
- On the General tab, in the Temporary Internet Files box,
click Settings.
At the top of the
Settings dialog box, there are four
choices.
Figure 3. Internet Explorer Cache Options
The vast majority of users leave the setting at the default of
automatically.
The most important fact to keep in mind is that these four options mostly
impact the behavior when there are no caching headers on the HTTP responses;
when caching headers are present, Internet Explorer will always respect them.
The following table describes the impact of these settings on request behavior.
Table 3. Cache behaviors
|
Setting
|
Cache copy is
fresh
|
Cache stale
|
No
cache-directives were present
|
|
Every visit to the page
|
No request
|
Conditional request
|
Conditional request
|
|
Every time you start
Internet Explorer
|
No request
|
Conditional request
|
Conditional request
|
|
Automatically
|
No request
|
Conditional request
|
Heuristic (see below)
|
|
Never
|
No request
|
Conditional request
|
No request
|
Cached content is considered
fresh if the request is made
during the freshness lifetime specified by the
Cache-Control
or
Expires headers on the original response. Cached content is
considered
stale if the request is made after the end of the
freshness lifetime specified.
The
automatically setting bears some explanation—how can
WinInet know if the cached resource is fresh when no caching directives were
provided on the server's HTTP response?
The answer is that WinInet can't know for sure and a Heuristic process is
followed to make a "best guess" effort. In the
automatically
state, the Heuristic will issue a conditional request unless all of the
following criteria are met:
- The cached
resource bears a Content-Type
that begins with image/.
- The cached
resource has a Last-Modified
time.
- The URL to
the cached resource does not contain a question mark (hinting that it's a
CGI request).
- The cached
resource has been conditionally requested at least once within the most
recent 25 percent of its overall age in the cache.
If all of the criteria above are met, no request is made.
As a Web developer, you should always ensure that you send appropriate
caching headers to guarantee you get optimum cache behaviors.
Flagging Performance Problems
You can use Fiddler's Custom Rules to draw attention to potential
performance problems. For instance, you can flag any response larger than 25KB.
To add this rule, click
Rules and then
Custom Rules,
and add the following code inside the
OnBeforeResponse event
handler:
// Flag files over 25KB
if (oSession.responseBodyBytes.length > 25000){
oSession["ui-color"] = "red";
oSession["ui-bold"] = "true";
oSession["ui-customcolumn"] = "Large file";
}
Similarly, you can flag responses that do not specify caching information:
// Mark files which do not have caching information
if (!oSession.oResponse.headers.Exists("Expires") &&
!oSession.oResponse.headers.Exists("Cache-Control")){
oSession["ui-color"] = "purple";
oSession["ui-bold"] = "true";
}
Introduction to HTTP Compression
All popular Web servers and browsers offer support for HTTP Compression.
HTTP Compression can dramatically decrease the number of bytes that are
transmitted between the server and the client; savings of over 50 percent for
HTML, XML, CSS, and JS are common.
A Web browser signals to the server that it is willing to accept HTTP
compressed content by listing the supported compression types in the request
headers. For instance, consider the following request to the new
MSN Search homepage:
GET / HTTP/1.1
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
Host: search.msn.com
The
Accept-Encoding header indicates Internet Explorer is
willing to accept responses that have been compressed using either the GZIP or DEFLATE
formats.
The MSN Search server obligingly returns the compressed contents; the
Content-Encoding
response header indicates the GZIP format was used:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/6.0 --Microsoft-HTTPAPI/1.0
X-Powered-By: ASP.NET
Vary: Accept-Encoding
Content-Encoding: gzip
Date: Tue, 15 Feb 2006 09:14:36 GMT
Content-Length: 1277
Connection: close
Cache-Control: private, max-age=3600
Using Fiddler, you can decompress the response using the Transformer tool on
the Session Inspector tab.
Figure 4. Transformer Inspector before decompressing GZIP'd response
Click the
No Compression radio button to decompress the
response inside Fiddler. Compression reduced the number of bytes transferred by
over 57 percent.
Figure 5. Transformer Inspector after decompressing response
The savings are more dramatic for the
common.css file used
by the MSN Search homepage; the CSS file was compressed 81 percent (from 25,288
bytes to 4,648 bytes). Note that image files like GIFs, JPEGs, and PNGs
generally are already compressed and thus usually are not delivered with HTTP
compression.
You can use Fiddler to simulate HTTP compression by checking "Simulate
GZIP Compression" on the Fiddler Rules menu.
Enabling compression for static files in IIS has a minimal CPU impact on IIS
Web servers, because the files are compressed only the first time and then
cached on the server. Enabling compression for dynamic files like ASP.NET pages
may impact your servers' CPU performance; you'll want to evaluate this
performance impact before enabling dynamic compression on production Web
servers.
Winning the Performance Battle
Improving HTTP efficiency is only half of the challenge; if your Web
application itself is slow, it won't matter how efficient your HTTP traffic is.