Tuesday, March 4, 2014

Compress your web pages for better performance


Users always want faster access to web resources. If your website is sluggish and serves pages slowly, would-be visitors won't wait, and will go elsewhere instead. Fortunately, you can employ several tools to compress your code and output and thus send fewer bytes over the Net, enhancing download times and creating a better user experience.
We'll look at tools that can:
  • Set your web server to compress (zip) all of its output.
  • Minify your JavaScript code, to yield a shorter but equivalent version.
  • Clean up your CSS rules and HTML code and remove unnecessary contents.
Some of the tools you must apply before deploying your site, and some require configuration changes to Apache or whatever web server you use. We won't be going into any tools that require changing your program logic (for example, using jQuery to dynamically load JavaScript on demand, for a shorter initial download) because that approach can inject interesting bugs into your pages.

Deflate your output

Whenever a client browser requests a page from a server, it can specify whether it will accept compressed data (meaning that the client can decompress whatever it receives) by means of the Accept-Encoding request header. The server, if it is able to produce the requested compression, will include the Content-Encoding header in the returned data, showing what method it applied to the data.
You can easily see how this works with the wget command and Wazi's own servers. If you ask for the page without encoding, you get 76,250 bytes (about 75K):
> wget -S -nv -O wazi.html www.openlogic.com/wazi
  HTTP/1.1 200 OK
  Cache-Control: private
  Content-Type: text/html; charset=utf-8
  Server: Microsoft-IIS/7.5
  X-AspNet-Version: 2.0.50727
  X-Powered-By: ASP.NET
  Date: Sun, 16 Feb 2014 00:45:24 GMT
  Transfer-Encoding:  chunked
  Connection: keep-alive
  Connection: Transfer-Encoding
2014-02-15 22:45:24 URL:http://www.openlogic.com/wazi [76250] -> "wazi.html" [1]
However, if you include the Accept-Encoding header, you get the same data, but gzipped, reduced more than 80% to 14,315 bytes, or less than 14K:
> wget -S -nv -O wazi.compressed --header "Accept-Encoding: gzip, deflate, compress" www.openlogic.com/wazi
  HTTP/1.1 200 OK
  Cache-Control: private
  Content-Type: text/html; charset=utf-8
  Server: Microsoft-IIS/7.5
  X-AspNet-Version: 2.0.50727
  X-Powered-By: ASP.NET
  Vary: Accept-Encoding
  Content-Encoding: gzip
  Content-Length: 14315
  Date: Sun, 16 Feb 2014 00:46:10 GMT
  Connection: keep-alive
2014-02-15 22:46:10 URL:http://www.openlogic.com/wazi [14315/14315] -> "wazi.compressed" [1]
All commonly used web browsers support this kind of compression, and routinely ask servers if they can provide compressed data, so enabling this feature for your site is an easy win. To do so with Apache you have to enable mod_deflate; include a LoadModule deflate_module /path/to/your/mod_deflate.so line in your httpd.conf file. The path for the module depends on your distribution, but you can easily determine it by checking out the other current LoadModule lines. If the module doesn't exist, use your favorite package manager to get it; mod_deflate is standard, and available in distribution repositories.
You also have to specify what kind of MIME type results should be compressed. Edit your .htaccess file to include a list like this:

  AddOutputFilterByType DEFLATE application/javascript 
  AddOutputFilterByType DEFLATE application/rss+xml 
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/xml 
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE text/css 
  AddOutputFilterByType DEFLATE text/html 
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE text/plain 
  AddOutputFilterByType DEFLATE text/richtext
  AddOutputFilterByType DEFLATE text/x-component 
  AddOutputFilterByType DEFLATE text/xsd 
  AddOutputFilterByType DEFLATE text/xsl 
  AddOutputFilterByType DEFLATE text/xml 
  AddOutputFilterByType DEFLATE image/svg+xml 
  AddOutputFilterByType DEFLATE image/x-icon

Not all files should be compressed; for example, compressing an already compressed ZIP file before sending it to the client would be a waste of time.
Restart your Apache server, and from that moment on, the web server will be able to honor all data compression requests. If you're using another web server, check the project's documentation; changes along the lines of what we did for Apache will be in order, but since all major servers support data compression, enabling it shouldn't be a hard task.

Minify your JavaScript

Even if you are deflating its output, the files that your web server has to send should be as small as possible. Many tools can minify your JavaScript code, producing an equivalent but smaller version. As a side benefit, the software can obfuscate files, meaning that they will be harder to understand by third parties, thus providing some degree of intellectual property protection, and it can optimize them.
YUI Compressor 2.4.8, developed by the Yahoo! User Interface group, is provided as a Java jar file that you run on Linux, Windows, or OS X. To test it, I used the latest jQuery version 2 uncompressed file, which runs nearly 240K for 10,000 lines of code. For the test I purposely got the bigger development version of jQuery; for actual use on your site you would use the 81K production version.
To learn about YUI Compressor's options, type java -jar yuicompressor-2.4.8.jar --help. For my test I ran java -jar yuicompressor-2.4.8.jar -o jquery.yui.js jquery-2.1.0.js, and it produced a 127K file, about 50% size of the original. Just to give you a taste of minified code, the start of the new file looks like this:
 * jQuery JavaScript Library v2.1.0
 * http://jquery.com/
 * Includes Sizzle.js
 * http://sizzlejs.com/
 * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
 * Released under the MIT license
 * http://jquery.org/license
 * Date: 2014-01-23T21:10Z
(function(b,a){if(typeof module==="object"&&typeof module.exports==="object"){module.exports=b.document?a(b,true):function(c){if(!c.document){throw new Error("jQuery requires a window with a document")}
return a(c)}}else{a(b)}}(typeof window!=="undefined"?window:this,function(window,noGlobal){var arr=[];var slice=arr.slice;var concat=arr.concat;var push=arr.push;var indexOf=arr.indexOf;var class2type={
};var toString=class2type.toString;var hasOwn=class2type.hasOwnProperty;var trim="".trim;var support={};var document=window.document,version="2.1.0",jQuery=function(selector,context){return new jQuery.f
n.init(selector,context)},rmsPrefix=/^-ms-/,rdashAlpha=/-([\da-z])/gi,fcamelCase=function(all,letter){return letter.toUpperCase()};jQuery.fn=jQuery.prototype={jquery:version,constructor:jQuery,selector:
"",length:0,toArray:function(){return slice.call(this)},get:function(num){return num!=null?(num<0 args="" arguments="" callback.call="" callback="" e="" each:function="" elem="" elems="" first:function="" function="" i="" j="" jquery.each="" jquery.map="" last:function="" len="this.length,j=+i+(i<0?len:0);return" map:function="" num="" onstructor="" pushstack:function="" q:function="" ret.prevobject="this;ret.context=this.context;return" ret="" return="" slice.apply="" slice.call="" slice:function="" this.eq="" this.length="" this.pushstack="" this="" var="">=0&&j
Three online JavaScript minifying service alternatives The javascript-minifier page lets you paste your code and get a minified alternative; for a command-line process you can also invoke it as a web service, through a POST request. Packer is another online alternative, which you can also use as a PHP, Perl, or .NET application. And jscompress is based upon both Packer and JSMin, an old tool by Douglas Crockford.
Note that comments starting with /*! are kept, so copyright and license texts won't be taken out. (Other compressors may not make that distinction, and may take out all comments, whatever their origin.) The produced code is more compact than the original, and if it were further compressed by Apache as we saw earlier, it would go down to about 30K, which is just an eighth of the original size.
We'll revisit YUI Compressor again when we consider how to compress CSS, but let's now look at another compressor that offers even more options and code trimming.
Google's Closure is in fact more than a minifier, because it not only removes extra white space, line end characters, and the like, it also revises your JavaScript code into better JavaScript by analyzing your code, removing useless bits, and rewriting whatever's left into a smaller, tighter, more efficient version. As an extra, it warns about possible JavaScript problems. You can use Closure as a web service or a RESTful API, but I went with a command-line Java jar version. The latest version is dated 1/10/2014, so it's quite up-to-date.
Closure has far too many options to list; after unzipping your download, run java -jar compiler.jar --help to check them out. Working again with the jQuery source code, I ran java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js jquery-2.1.0.js --js_output_file=jquery.closure.advanced.js and got a file of about 75K, which is smaller than jQuery's own minified version. Here is a sample of the code. Notice that it is even harder to understand than the YUI Compressor version; in advanced compilation mode, Closure changes variable and function names, inlines code, and performs several optimizations that go beyond minifying.
(function(q,W){"object"===typeof module&&"object"===typeof module.jc?module.jc=q.document?W(q,!0):function(q){if(!q.document)throw Error("jQuery requires a window with a document");return W(q)}:W(q)})("
undefined"!==typeof window?window:this,function(q,W){function ka(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)}function X(a,b){a=b||a;return"none"===d.c(a,"display")||!d.contains(a.owne
rDocument,a)}function Gb(a,b){return b.toUpperCase()}function d(a,b){return new d.b.la(a,b)}function Ba(a){var b=
a.length,c=d.type(a);return"function"===c||d.N(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"===typeof b&&0")).ob(b.documentEle
ment),b=T[0].contentDocument,b.write(),b.close(),c=Wa(a,b),T.detach()),Ya[a]=c);return c}function U(a,b,c){var f,e,g=a.style;(c=c||ka(a))&&(e=c.getPropertyValue(b)||c[b]);c&&(""!==e||d.contains(a.ownerDoc [...]
By employing this kind of tool, your development team can work with clear, fully commented and indented versions of your source code, while end users get a much shorter version that's optimized for speed – a win-win situation!

Compress CSS and HTML

Your website consists not only of JavaScript, but also CSS rules and HTML code, so to round things out let's see how to compress such content. YUI Compressor, which we already looked at for JavaScript, can work with CSS files too. I got one of OpenLogic's own CSS files, which is 22K in size and about 1,400 lines long, and ran java -jar yuicompressor-2.4.8.jar wazi.css -o wazi.yui.css. That got the file down to 17K, a 25% size reduction, in a single extra long line.
.PreviewPanel{border-right:silver thin solid;border-top:silver thin solid;border-left:silver thin solid;border-bottom:silver thin solid}.linksubmission{border:solid 0 red;font:normal 11px Tahoma,Arial,V
erdana,sans-serif}.linksubmission IMG{border:solid 0 red !important}.linksubmission A{border:solid 0 red !important;text-decoration:none !important;font:normal 11px Tahoma,Arial,Verdana,sans-serif}.Grid
Header_Monochrome a:visited,.GridHeader_Monochrome a:hover{color:white !important;font:bold 11px Tahoma !important}.GridPager_Monochrome a:visited,.GridPager_Monochrome a:hover{font:normal 11px Tahoma !
important}.popupHelpTitle{padding-bottom:5px;font-weight:bold;color:black}.nostyle{border:0 solid red !important}.nostyleimg{border:0 solid red !important}.CommandItem{background-color:Transparent;backg
round-image:none}.Grid{border:1px solid #7c7c94;background-color:#fff;cursor:pointer}.HeadingRow{background-color:#e2e2e2}.HeadingCell{background-color:#e2e2e2;border:1px solid #fff;border-right-color:#
rder-right:1px solid #eae9e1;border-bottom:1px solid #eae9e1;font-family:verdana;font-size:10px}.EditDataCell{padding:0 !important;background-color:#e2e2e2;border-width:0 !important}.EditDataField{paddi
ng:0;padding-left:1px;font-family:verdana;font-size:10px;height:20px;width:98% !important}.DataRow td.FirstDataCell{padding-left:3px}.SelectedRow{background-color:#ffeec2}.SelectedRow td.DataCell{cursor
:default;padding:2px;padding-left:3px;padding-bottom:3px;font-family:verdana;font-size:10px;border-bottom:1px solid #4b4b6f;border-top:1px solid #4b4b6f;border-right:0}.SelectorCell{background-color:#e2
e2e2;border:1px solid #fff;border-right-color:#b5b5b5;border-bottom-color:#b5b5b5}.GridFooter{cursor:default;padding:5px}.GridFooter a{color:Black;font-weight:bold;vertical-align:bottom} [...]
I also tried two online utilities: CSS Minifier and CSS Compressor. The former can be used from the command line or as a web service by doing a POST request. The latter doesn't offer that option but includes extra optimizations, such as changing "0px" to "0," or "#FFFFFF" to "#FFF," and even "#808080" to "gray" (but "black" to "#000"!) so it goes after every possible byte to be reduced.
For HTML code, htmlcompressor can minify HTML and XML source, and is available as a command-line Java jar file for local usage. It has a large number of available options; type java -jar htmlcompressor-1.5.3.jar --help to check them out. If you are undecided as to which options use, try the -a option, which produces a nice analysis that can help indicate which command-line options you should use. I tested it with the OpenLogic HTML file I used in my deflate tests by using the command java -jar htmlcompressor-1.5.3.jar wazi.html -a. I got the following report:
         Setting          | Incremental Gain |    Total Gain    |  Page Size   |
Compression disabled      |         0 (0.0%) |         0 (0.0%) |       75,462 |
All settings disabled     |        79 (0.1%) |        79 (0.1%) |       75,383 |
Comments removed          |     2,042 (2.7%) |     2,121 (2.8%) |       73,341 |
Multiple spaces removed   |     2,492 (3.4%) |     4,613 (6.1%) |       70,849 |
No spaces between tags    |       457 (0.6%) |     5,070 (6.7%) |       70,392 |
No surround spaces (min)  |         0 (0.0%) |     5,070 (6.7%) |       70,392 |
No surround spaces (max)  |         4 (0.0%) |     5,074 (6.7%) |       70,388 |
No surround spaces (all)  |       147 (0.2%) |     5,221 (6.9%) |       70,241 |
Quotes removed from tags  |     1,101 (1.6%) |     6,322 (8.4%) |       69,140 |
 attr. removed      |        95 (0.1%) |     6,417 (8.5%) |       69,045 |

The compressed HTML code is about 10% shorter.
Note that with all these tools, if your site's HTML code is generated by a script, a compressor won't be able to compress it effectively.

In conclusion

As you can see, you can take advantage of several ways to reduce file sizes and in effect speed up your web server. The sum of all compression functions – server, JavaScript, CSS, and HTML – produces smaller data transmissions and faster response times. Apply them, and your users will thank you for it!

No comments:

Post a Comment