Tech Note: Make Web Pages Load Faster by Compressing CSS
I originally started writing this Tech Note just to test our new source code formatting feature (powered by jQuery.Syntax), but it turned into more of a tech article than a note. I’ll be describing a technique for making pages load faster, that should be of interest to any web developer with experience in PHP and Linux shell script programming.
CSS Files Grow Up So Fast
Almost all modern websites use a technology named CSS (Cascading Style Sheets) to control the look of pages on the site. A CSS file is just an ordinary text file, consisting of rules that determine the appearance and position of page elements. For example, here are the rules that control the look of hyperlinks at LGF:
a {
font-weight: bold;
text-decoration: none;
padding: 0 1px;
}
a:link {
color: #06C;
}
a:visited {
color: #09C;
}
a:hover {
color: #FFF;
background-color: #65BD54;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
a:active {
color: #006600;
background-color: transparent;
}
As you add more elements to your design, your CSS file (or files - there may be more than one) can get quite large, especially if you use lots of comments. The larger the file, the longer it takes to send over the Internet to a visitor’s browser, so optimizing CSS files can have a big payoff in terms of visitor experience.
You can do some basic optimization just by following good CSS coding practices, such as using shorthand rules and minimizing white space. But there’s a point at which further optimizations make the code difficult to read and modify; to name just one case, linefeeds are optional in a CSS file and can be completely eliminated, but removing all linefeeds means the entire file shows up on one monstrously long line. Browsers happily parse that monster line but it’s a nightmare scenario for human beings. Browser happy, but developer has a sad.
That’s where this Tech Note comes in.
The solution to the expanding CSS problem
I’m going to show you how to have the best of both worlds — fully commented, nicely readable and editable CSS code and highly optimized but almost unreadable (by humans) code that is much smaller and loads and executes faster. The key is to generate the optimized file whenever you make changes, leaving the editable, un-optimized version intact.
An alternative method is to dynamically generate the compressed version by embedding PHP code directly into the CSS file; but this incurs quite a bit of processing overhead every time the CSS file is loaded, and isn’t very scaleable as traffic increases. The method described here will generate a static CSS file with no extra overhead.
Squeeze it with PHP
Here’s the first step in the process; a short PHP script that optimizes CSS files by stripping out all comments and all unnecessary white space, including linefeeds and tabs. When I use this script to process LGF’s CSS file, the resulting file is about 20% smaller than the original; results will vary based on how many comments there are, what type of white space you use (tabs or spaces), etc.
We’ll name this script compress.php
.
$css1 = array(
"\r\n",
"\r",
"\n",
"\t",
'{ ',
' {',
' }',
'; ',
';}',
': '
);
$css2 = array(
'',
'',
'',
'',
'{',
'{',
'}',
';',
'}',
':'
);
$input = '';
while (!feof(STDIN)) $input .= fgets(STDIN);
$input = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $input);
$input = str_replace($css1, $css2, $input);
echo $input;
The code’s pretty simple. Lines 2-25 define two arrays to use in replacing white space. The CSS file is read into the string variable $input
in line 27, comments are removed (with a regular expression and preg_replace
) in line 28, the white space is stripped out (with str_replace
) in line 29, and then the resulting compressed string is sent to standard output with the echo
command.
Calling PHP from the Linux command line
Those of you who speak PHP may notice that the script above never opens any files for reading or writing. Where is it getting the CSS code from? Where is it sending the optimized code to? How do it know?
The answer is that this little script is designed to run from a Linux command line (instead of the more common PHP environment, as a web page). It takes input from the system’s standard input stream, and sends output to, oddly enough, standard output. The global variable STDIN
(in line 27) is an already-opened file handle to standard input, thoughtfully provided by PHP to save you the trouble of opening and closing the stream yourself.
This lets you use the Linux shell’s piping feature to send the PHP script the contents of a CSS file, and then send the processed output to another file, with a simple command like this:
cat styles.css | php /path/to/script/compress.php > styles.min.css
This line reads in the styles.css file, pipes it to the PHP compression script, then writes the output of the script to the file styles.min.css. (It assumes the current directory is where the CSS file resides.)
Putting it together with a shell script
OK, that’s pretty cool, but let’s take it to the next level by combining several optimized CSS files into one, thereby decreasing the loading time even more (since loading one file is exponentially faster than loading several files).
To do this, we’re going to write a bash shell script, so we’ll be able to generate a new optimized CSS file at any time just by typing the shell script’s name at the command line.
And to keep that typing to a minimum, we’ll name this script cssm
(for “CSS Minimizer”):
#!/bin/bash
cd /path/to/cssfiles/
FILES="styles1
styles2
styles3"
for f in $FILES; do
cat "$f.css" | php -q /path/to/script/compress.php | tr -d "\n" >> tmp.css
echo -en "\n" >> tmp.css
done
mv -f tmp.css styles.min.css
If you actually use this script, of course, you’ll need to replace the paths and file names with the ones for your server, put the script somewhere in your bash include path, and give it executable permissions.
Then you can add the names of the CSS files (without the .css extensions) you want to compress/combine to the list variable FILES
; the script loops through the filenames and compresses each one, then appends it to tmp.css
, with a single line feed after each one just to make it easier to see where each compressed file starts. When the loop is finished, tmp.css
is renamed to the actual file your web page will use, styles.min.css
.
The safest way to replace web-visible files
Renaming the file this way is actually safer than directly creating a new version of styles.min.css
— because if a user’s browser happens to be reading your previous styles.min.css
while you’re writing to it, they’re likely to see a pretty screwed up web page or one with no styles at all. Creating a temporary file and then renaming it with the mv
command solves this problem because it’s an “atomic” operation in Linux; any applications that are currently reading the file when it’s renamed won’t be interrupted. This is the best way to replace files that are heavily used in a web server environment.