When a website loads, fetching the HTML takes less than 20% of the total response time; 80% goes to other components such as images, JS, CSS, and scripts. The key is to profile current performance and find where you can get the biggest improvement. So in this case, we should focus on front-end performance.
1. Make Fewer HTTP Requests
According to the performance golden rule, more than 80% of the time is spent on requests for the components referenced by the HTML document. So the simplest way to improve response time is to reduce the number of components, and thus the number of HTTP requests. Techniques include image maps, CSS sprites, inline images and scripts, and combining stylesheets.
Image maps and CSS sprites combine images to reduce the number of components and thus HTTP requests
Inline images use data: URLs to include images in a web page without extra HTTP requests. Because they're inlined in the page, they aren't cached across pages, and you shouldn't inline your company logo since it makes pages larger.
Combine scripts and stylesheets, even though this goes against the modular approach of software development. The solution is to follow a build step that compiles files while keeping your JS modular.
2. Use a CDN
Web servers distributed across different geographic locations deliver content to users more efficiently.
This improves performance, shortens response times, and reduces costs. The downsides are that response times can be affected by other websites, and you can't modify the HTTP response headers.
3. Add an Expires Header
Browsers use caching to reduce the number of HTTP requests and the size of HTTP responses, making web pages load faster. A web server uses the Expires header to tell the web client it can use its copy of a component until the specified time. In short
: after that time, the resource expires.
Expires improves performance well, but it requires the server and client clocks to be strictly in sync. The expiration date also needs to be checked often, and once that day arrives, you have to provide a new date in the server config.
Is there a better way?
Of course: Cache-Control with max-age specifies how long a resource is cached, in seconds. Within max-age, the cache is used. When both are present, Cache-Control takes priority.
Expires makes heavy use of caching and reduces HTTP requests, but there's a problem: when a resource changes, how do you update it? Mark Nottingham's advice in "Caching Tutorial for Web Authors and Webmasters" is:
The most effective solution is to change all links to it, so that brand-new requests download the latest content from the origin server
At Yahoo, the file names of all components use variables. To update, you just change the variable in one place and embed the version number in the component. This not only changes the file name but also makes it easier to find the exact source file when debugging.
4. Gzip Components
Gzip reduces response time by making responses smaller. Web clients indicate support for compression with the Accept-Encoding header in the HTTP request.
request:Accept-Encoding:gzip, deflate
response:Content-Encoding: gzip
5. Put Stylesheets at the Top
Putting stylesheets at the bottom of the page blocks progressive rendering. You can fix this by moving them into the document's HEAD.
6. Put Scripts at the Bottom
Scripts such as JavaScript cause a similar problem, but the solution is the opposite: move scripts from the top of the page to the bottom.
With stylesheets, progressive rendering is blocked until all stylesheets have downloaded. That's why it's best to move stylesheets into the document's HEAD, so they download first without blocking rendering. With scripts, progressive rendering is blocked for all content below the script. The lower on the page you put scripts, the more content can render progressively
The number of page components has the biggest impact on response time. Browsers make HTTP requests in parallel, and the HTTP 1.1 spec suggests browsers download two components in parallel per host. Many web pages download all their components from one host, which is why you see HTTP requests in a staircase pattern.
Parallel downloading is disabled for scripts; even with different hostnames, the browser won't start other downloads. This ensures scripts execute in the correct order.
7. Avoid CSS Expressions
8. Use External JavaScript and CSS
Whether to use inline or external JS and CSS depends on three factors: page views, empty vs. full cache, and component reuse
The more page views, the better external files work
The higher the share of visits with a primed cache, the better external files work
If every page uses the same JS and CSS, the higher the component reuse, the better external files work
9. Reduce DNS Lookups
DNS lookups can be cached to improve performance. They can be cached by the ISP or on a dedicated caching server on the local network, but here we'll look at the operating system's cache.
The server can specify how long a record may be cached. The DNS record returned by a lookup includes a time-to-live, which tells the client how long it can cache the record.
10. Minify JavaScript
Minification removes unnecessary characters from code to reduce its size and improve load time, such as deleting comments and unnecessary whitespace.
Compression with gzip has the biggest impact, but minification reduces file size even further.
To minify CSS, merge identical classes, remove unnecessary classes, and so on.
11. Avoid Redirects
A redirect reroutes the user from one URL to another. There are many kinds of redirects; 301 and 302 are the most common. Redirects slow down your pages.
12. Remove Duplicate Scripts
Two main factors lead to duplicate scripts: team size and the number of scripts.
Duplicate scripts hurt performance in two ways: unnecessary HTTP requests and time wasted executing JavaScript.
One way to avoid duplicate scripts is to implement a script management module in your templating system that ensures each script is included only once. Using a getVersion function is also recommended: it looks up the script and appends the matching version number. As mentioned above, you can set a far-future Expires header, so whenever a file's content changes, its file name must change too, which you can do by adding a version number to the component.
13. Configure ETags
An ETag (entity tag) is a mechanism web servers and browsers use to validate cached components.
ETags check whether a component in the browser cache matches the one on the origin server. An ETag is a string that uniquely identifies a component, and it adds a more flexible mechanism. It's used with the If-None-Match request header. If the ETag matches, the server returns 304 Not Modified
In a web server cluster, ETag values don't match across servers. For example, with n servers in your cluster, the chance that a user's cached ETag matches the server next time is 1/n. It also reduces the efficiency of proxy caches. When ETags don't match, the request goes to the origin server, and from origin to proxy to user, two 200 responses are produced.
So to use ETags, you must configure them properly to get the best results.
14. Make Ajax Cacheable
Ajax stands for "Asynchronous JavaScript and XML," though the best-known alternative to XML is JSON. Ajax isn't a single licensed technology but a group of technologies, including JavaScript, CSS, the DOM, and asynchronous data fetching. Its goal is to break through the web's inherent start-stop interaction. Showing users a blank screen and then redrawing the entire page isn't a good user experience. Ajax inserts a layer between the UI and the web server. This Ajax layer lives on the client, interacts with the web server to fetch requested information, and interacts with the presentation layer to update only the necessary components. It turns the web experience from "browsing pages" into "interacting with an application."
Asynchronous doesn't mean instantaneous (Asynchronous = Instantaneous?)
An obvious advantage of Ajax is that it gives users instant feedback, because it requests information from the backend asynchronously.
The key factor in whether users have to wait is whether the Ajax request is passive or active. Passive requests are made in advance for future use; for example, a web-based email client might passively request the user's address book before they actually need it. Active requests are made based on the user's current action, such as finding all emails matching a user's search.
Make sure Ajax requests follow the performance guidelines, especially having a far-future Expires header.
Note: these are my reading notes on "High Performance Web Sites"
