Compressed JavaScript Compression

by Jeff Starr on Tuesday, April 24, 2007 32 Responses

In this article, we extrapolate our favorite CSS-compression technique for JavaScript. Below, we outline the steps required to auto-compress your JavaScript documents via gzip and PHP. Two different compression methods are presented. The first method does not require htaccess, but rather involves the manual editing of JavaScript files. The second method employs htaccess to do all the work for you, thus requiring much less effort to implement. In either case, the result is the same: automatically compressed content delivered only to supportive browsers, resulting in reduced bandwidth, faster loading times, and smiling visitors :)

Method One

Overview: This method involves adding a small PHP script to your JavaScript document and replacing its .js extension with a .php extension.

Place the following PHP script into the top of any JavaScript document(s) that you wish to compress. Then change the .js extension to .php, to arrive at something similar to compressed-js.php. Remember to use the new name when referencing the file (i.e., <script type="text/javascript" src="compressed-js.php"></script>):

<?php 
   ob_start ("ob_gzhandler");
   header ("content-type: text/javascript; charset: UTF-8");
   header ("cache-control: must-revalidate");
   $offset = 60 * 60;
   $expire = "expires: " . gmdate ("D, d M Y H:i:s", time() + $offset) . " GMT";
   header ($expire);
?>

Here is the same PHP script commented with functional explanations:

<?php

   // initialize ob_gzhandler function to send and compress data
   ob_start ("ob_gzhandler");

   // send the requisite header information and character set
   header ("content-type: text/javascript; charset: UTF-8");

   // check cached credentials and reprocess accordingly
   header ("cache-control: must-revalidate");

   // set variable for duration of cached content
   $offset = 60 * 60;

   // set variable specifying format of expiration header
   $expire = "expires: " . gmdate ("D, d M Y H:i:s", time() + $offset) . " GMT";

   // send cache expiration header to the client broswer
   header ($expire);

?>

Functional Summary: The previous PHP function will first check to see if the browser requesting the file will accept "gzip-deflate" encoding. If no such support is detected, the requested file is sent without compression. Next, the function sends a header for the content type and character set (in this case, "text/javascript" and "UTF-8"). Then, a "must-revalidate" "cache-control" header requires revalidation against currently specified variables. Finally, an "expires" header specifies the time duration for which the cached content should persist (one hour in this case).

Method Two

Overview: This method involves placing the PHP script in a separate .php file and adding a set of rules to an .htaccess file.

A more discrete, unobtrusive method for compressing JavaScript involves two steps. First, save the script provided in the first method (above) as a seperate gzip-js.php file and place it in a JavaScript-exclusive directory. Then, add the following ruleset to an .htaccess file located in the same JavaScript-exclusive directory (i.e., the JavaScript directory should contain only .js files):

# JavaScript compression htaccess ruleset
AddHandler application/x-httpd-php .js
php_value auto_prepend_file gzip-js.php
php_flag zlib.output_compression On

Here is the same .htaccess ruleset commented with functional explanations:

# JavaScript compression htaccess ruleset

# process all JavaScript files in current directory as PHP
AddHandler application/x-httpd-php .js

# prepend the PHP script to all PHP files in the current directory
php_value auto_prepend_file gzip-js.php

# compress all parsed PHP pages from current directory
# this rule is redundantly present as the first line of the PHP script
php_flag zlib.output_compression On

Functional Summary: The .htaccess rules above first instruct Apache to parse all JavaScript files in the current directory as PHP. After this, Apache is instructed to insert the contents of the “gzip-js.php” file into the beginning of each PHP file (i.e., files with .js extensions renamed to .php) parsed from the current directory. And finally, Apache is instructed to compress automatically every parsed document in the current directory.

Confirmed Browsers

  • Internet Explorer 5 and up: works great
  • Netscape Navigator 6 and up: works fine
  • Mozilla/Firefox: all versions seem to work
  • Opera: does not cache compressed content

References


32 Responses

Add a comment

[ Gravatar Icon ]

oakleaf#1

It seems that when using the first method, on FF Mac, the javascript and css files are compressed, but not cached.

Am I wrong ?

[ Gravatar Icon ]

Perishable#2

As far as I know, Firefox will cache gzipped content delivered via either method, however Opera will not, regardless of method.

[ Gravatar Icon ]

oakleaf#3

I use FireBug to see what’s cached and what’s not, and my style.php is not (according to the color of the bar).

I may be wrong but anyway, thank you for your fast answer, and also, thank you for that great blog you write.

[ Gravatar Icon ]

oakleaf#4

Note: I think your sk2 plugin make the

“Save information for future comments”

and the

“Notify me of followup comments via e-mail”

checkboxes stop working.

[ Gravatar Icon ]

Perishable#5

I am looking into this.. some questions for you:

1. You mention that the first method does not work - have you tried the second method?

2. You mention that your "style.php" is not being cached - are you sure you have properly implemented the method (e.g., correct file(s), name(s), etc.) - this post describes JavaScript compression - "style.php" sounds like it may contain CSS content?

3. Does Firefox cache uncompressed content from your site?

4. What method(s) are you using to send the proper caching headers for your content?

I also appreciate the information from your second comment. Some questions for you:

1. I have just finished testing the issues you mention and could not find a problem - did you happen to clear your cookies in between sessions (for the "remember me" dilemma)?

2. The WP Admin shows that you are indeed subscribed to comments on this post - are you not receiving the email notifications?

3. You mention sk2 plugin - if there were a problem, how would sk2 be involved? do you know of any potential conflicts between parties?

I find your concerns very important. Thank you for helping me look further into these issues.

[ Gravatar Icon ]

oakleaf#6

You are really kind taking me in such consideration… I appreciate it a lot !

1. The second method doesn’t work because of my hosting (1000GP mutualised at OVH)

2. I’ve posted in this post by mistake, but be sure i used the css method in the stylesheet, and the js method in my javascript file.

3. Firefox cache the files uncompressed (.css, .js), but don’t cache the compressed one (.php).

4. I use exactely what you advice, just changed the cache-duration to 3600*24*7.

1. No, i never clear cookies manualy.

2. The cookies has been “sent to me”, on my second comment (the ones in a row).

3. I think the captcha that appeared before I had cookies broke the process of the WP comment system.

-> When I first commented on this post, I checked the boxes, then clicked “submit”. I was redirected to a blank page with a captcha. When I submitted the captcha, my comment was posted, but I think the process of storing cookie on my computer was broken

I hope it will help you :)

[ Gravatar Icon ]

oakleaf#7

Note again: I’ve registered, so i guess there won’t be any captcha anymore ;)

[ Gravatar Icon ]

Perishable#8

oakleaf,

My tests indicate that Firefox 2 is indeed caching files compressed via gzip, regardless of method. Using the first method with an offset value of 3600*24*7, Firebug provides the following data when the compressed file is loaded into the browser:

Date: Wed, 23 May 2007 19:38:05 GMT
Cache-Control: must-revalidate
Content-Encoding: gzip
Expires: Wed, 30 May 2007 19:38:05 GMT

Notice the time difference between Date and Expires - precisely one week, as specified by the offset.

To verify further, clear your Fx2 cache and open about:cache via the address bar. After verifying a zero cache sum, load the gzipped page in question and take another look at the cache contents. The compressed file now should appear in the "Disk cache device" cache.

[ Gravatar Icon ]

oakleaf#9

You are totally right, and I could’ve found it out myself :/

But anyway, it’s strange that the style.php appears in dark grey in FireBug, maybe it colors the elements just by their extensions…

Thank you very much for your work !

[ Gravatar Icon ]

Perishable#10

My pleasure — glad to be of help!

[ Gravatar Icon ]

August Klotz#11

Just a note:
In the first JavaScript-compression method you employ the function ob_start("ob_gzhandler"); without first testing for the presence of the required Apache extension, zlib. Replacing that first line with a simple check is an easy way to prevent unnecessary errors during implementation. Something such as the following would definitely do the trick:

<?php if(extension_loaded('zlib')){ob_start('ob_gzhandler');} ?>

[ Gravatar Icon ]

Taneem#12

Hello Mate,
After adopting method 1, I can see php file of the js file in my browser cache. The filesize of it remains the same. How would I understand the file has been zipped at some point? Thank you.

Kind Regards
Taneem

[ Gravatar Icon ]

Perishable#13

Hi Taneem, have you tried using a browser tool such as Firebug? With Firebug installed, there is a Firefox extension called YSlow that enables you to analyze many different components of your web pages, including file compression.
Regards,
Jeff

[ Gravatar Icon ]

Runar#14

Greetings,

I had to remove the “ob_gzhandler” part from ob_start() in the gzip-js.php file, because I received this notice:

Warning: ob_start(): output handler 'ob_gzhandler' conflicts with 'zlib output compression' in /blabla/gzip-js.php on line 2

When I edited that out, it works perfectly.

[ Gravatar Icon ]

Perishable#15

Yes, that error message indicates that compression is already enabled elsewhere on your server, either through Apache or via your php.ini file. Either way, thanks for the information — glad to hear that everything is working :)

[ Gravatar Icon ]

Chris#16

Wrap all PHP in a Try Catch block and you’ll be fine ;)

That’s what I did and works fine for me. Who knows if it was really compressed or not haha.

Thanks for this.

[ Gravatar Icon ]

Perishable#17

My pleasure, Chris — thanks for the feedback! :)

[ Gravatar Icon ]

Carro Brasilia#18

Any way to stop compressing .js via Apache? I’m getting problem with protoculuos.

[ Gravatar Icon ]

Perishable#19

I have not yet tried this, but you could theoretically disable compression in any subdirectory via HTAccess. You could also try an alternative to protoculuos, if there is such a thing.

[ Gravatar Icon ]

free games#20

Google have a content distribution network here

http://code.google.com/apis/ajaxlibs/documentation/#AjaxLibraries

where you can get javascript served from googles fast servers compressed, it involves adding script like

google.load("jquery", "1.2.6");

Javascript I served last month amounted to 7.67gb, looking forward to a reduced bandwidth bill.

[ Gravatar Icon ]

Jeff Starr#21

Another excellent tip, free games! Keep ‘em coming! :)

[ Gravatar Icon ]

Tony#22

Jeff, I don’t care what my wife will say about this (I won’t show her just in case), but I absolutely love you!

For the past 48 hours, I’ve been googling for a way to compress the 46 KB mootools on my site. mod_gzip isn’t available as I have a shared account, and everything I tried didn’t work. Your first method here worked. I would have prefered the second, cleaner method, but the directory in question has a css file, not only .js. And it’s a plugin for WP, so I didn’t want to edit the plugin files, because every time I’ll update it I’ll have to repeat everything. I just edited it to change the extension to .php.

I don’t know why I didn’t find this article earlier through Google! In the future I’ll search with site:perishablepress.com in mind. :-D

Do you have some tutorial about how to compress all css and js files and deliver them in only 2 files? I have 6 css and 5 js on every page. They’re tiny files, except the mootools one, but it’s 11 requests!

Thanks!

[ Gravatar Icon ]

Tony#23

Oh and I tried the htaccess method in js exclusive directory. But a plugin is calling for a “jquery.js?ver=1.2.6″, not just “jquery.js”. Firebug is showing me that jquery.js?ver=1.2.6 is not found… If I only knew where it’s calling from!
Any idea how to resolve this?

Thanks in advance!

[ Gravatar Icon ]

Jeff Starr#24

@Tony: Hey, check out this method for combining and compressing CSS and JavaScript files. I haven’t tried it yet, but it looks very promising!

For the second question, search your files for the “jquery.js?ver=1.2.6” character string to determine the source of the call..

[ Gravatar Icon ]

Jack#25

Its not working in opera?

Any solution for that?

[ Gravatar Icon ]

Vladimir#26

Wouldn’t it be easy to configure Apache’s mod_deflate to compress JS/CSS?

IMHO it is much faster to serve the static file than load a PHP interpreter and process the script dynamically.

Say,

<IfModule mod_deflate.c>
       <IfModule mod_deflate.c>
              DeflateBufferSize 32768
              DeflateCompressionLevel 4

              <Location />
                     AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript application/x-javascript text/javascript
                     BrowserMatch ^Mozilla/4 gzip-only-text/html
                     BrowserMatch ^Mozilla/4\.0[678] no-gzip
                     BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
                     SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|tiff|flv|swf|zip|pdf)$ no-gzip dont-vary
                     Header append Vary User-Agent env=!dont-vary
              </Location>
       </IfModule>
</IfModule>

[ Gravatar Icon ]

Jeff Starr#27

Vladimir, no argument here, however not every server is equipped with mod_gzip/mod_deflate, and many that are do not have it enabled. An example, sadly, is my current shared host at A Small Orange. They do not enable compression other than thru ob_gzhandler for HTML/PHP output.

That method looks tight, and will try it on my shiny new Media Temple account. One note, you should only need one IfModule test for the deflate module. ;)

[ Gravatar Icon ]

Raina Gustafson#28

FYI… this method worked for me on a Media Temple gridserver account, while Vladimir’s and the PP .htaccess methods above did not.

Love the content of this site! Thanks for sharing your wisdom. :)

Trackbacks / Pingbacks
  1. My need for speed: 14 ways to speed up Joomla | Rants of a Loony Toon
  2. GZip files with .htaccess and PHP | Lateral Code
  3. Ultimate Wordpress Tutorial : How to really tweak your Wordpress ? - PsychedEric
  4. Matt the web designer » links for 2009-06-11
Share your thoughts..

Read Comment Policy

Comment Rules: No spam. No profanity. Use your real name. You may use simple HTML tags for style. Wrap all code in <code> tags. Learn more.



Previous post: Yes, I Took the Survey

Attention: Do NOT follow this link!