Search This Blog

Tuesday, January 27, 2009

How to Compress PHP and Other Text-Based Files with 1&1 (and Other Shared Hosts)


If you have a shared Linux hosting package through 1&1, you have no doubt discovered that the usual method of changing your .htaccess file does not allow you to compress your files using Apache because 1&1 has disabled the mod_deflate and mod_gzip modules; you also cannot specify php values in .htaccess files in 1&1 shared packages, so a solution like this one also will not work. However, there is a solution that does work, and it works even on the most basic 1&1 Beginner’s Package.

Note: If you plan to implement the Minify! option I blog about in How to Easily Combine, Minify, and Cache JavaScript and CSS..., you can safely skip steps 1 and 2 and include only the first line shown in step 3.

Step 1:

Determine what your root path is; if you are using a 1&1 package, it will look something like this:
/kunden/homepages/12/d123456789/htdocs/
You can do that by creating a text file in Notepad or other editor,* entering the following lines, then saving it with as phpinfo (or whatever you want) followed by the .php extension.
<?php
phpinfo();
?>
(* If you’re using Notepad, you’ll probably want to put quotation marks around the name "phpinfo.php" when saving, to keep it from saving the file as phpinfo.php.txt.

Personally, after passing from Geany on Windows (used Notepad++ some), and Komodo Edit on Mac OSX, my editor of choice is now a customized version of vim. Incidentally, the three previously mentioned IDEs mutually rely on the Scintilla editor as their base.)

Now browse to the newly uploaded file with your web browser and search for DOCUMENT_ROOT. This variable will tell you what the exact path is to your server.

Step 2:

Now create another php file in the same way, this time adding the following information so that when you compress your CSS and JS files, they are served with the right headers, or else they will not load properly in Firefox and other browsers:
<?php
if (isset($_SERVER['SCRIPT_FILENAME'])) {
    $timestamp = filemtime(__FILE__);
    header('Last-Modified: ' . $timestamp);
    $expires = 60*60*24*14;
    header("Pragma: public");
    header("Cache-Control: maxage=".$expires);
    header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$expires) . ' GMT');
    header('Vary: Accept-Encoding');
    $pathinfo = pathinfo($_SERVER['SCRIPT_FILENAME']);
    $extension = $pathinfo['extension'];
    if ($extension == 'css') {
        header('Content-type: text/css');
    }    
    if ($extension == 'js') {
        header('Content-type: text/javascript');
    }
}?>
Make sure that you don’t space down between the lines or add an extra space at the end, and save this file as headers.php, uploading it to wherever you keep your scripts. For the sake of this example, I will assume that you store your scripts in a directory named webscripts.

[Update: As Brett reminds me in the comment below, you have now given PHP control of rendering your pages, so you will also need to include in this file any other header information you know you’ll need by declaring each header on its own line. For example, notice this extra line:

header('Vary: Accept Encoding');

Based on the readership of these articles, this will be particularly useful when optimizing for speed using a free tool like Google's Page Speed and the Yahoo! based YSlow. Add as many extra headers as you need, one per line.]

Step 3:

Create a third file, this time a PHP configuration file, entering the following information (though of course changed with the information you looked up in step 1 and whatever directory you use for step 2—unfortunately, you can’t use environmental variables in .ini files, as they are straight ASCII):
zlib.output_compression=1
auto_prepend_file=/kunden/homepages/12/d123456789/htdocs/webscripts/headers.php
Name your configuration file the conventional php.ini and save it. (If for some reason zlib.output_compression=1 doesn't work, try output_handler=ob_gzhandler instead.)

The first line tells the server to use PHP’s built-in g-zip handler for compression; the second line points to the exact location of the file you created in step 2: /kunden/homepages/12/d123456789/htdocs/ is the document root you located in step 1, and webscripts/ is the directory where you uploaded the headers.php script.

auto_prepend_file tells the server to automatically “prepend,” or “attach to the beginning of” all pages it serves (versus auto_append_file, which would “append,” or “attach to the end” of all files). The if/then conditional statements in step 2 ensures that the headers.php file will only attach headers if the file in question has a .css or .js extension.

Note: Check with your web host. With 1&1, at least, you will need to upload a copy of your php.ini file to EVERY directory containing files you want compressed.

Step 4:

Create an .htaccess file or modify your existing one by adding the following line (if you’re not familiar with an .htaccess file, see here):

CAUTION: If you plan to implement the Minify! solution I blog about in How to Easily Combine, Minify, and Cache JavaScript and CSS..., I do not recommend including the .css and .js file extensions on this line. Doing so processes these files twice, as Minify! already has its own mechanism for compressing files. And, as a result of double compression, certain versions of Internet Explorer choke and die silently.

AddType x-mapp-php5 .php .shtml .html .htm .txt .js .css
This line tells the server to process all the file extensions specified through PHP5; change it to x-mapp-php4 if you want to use PHP4 instead. You can add other extensions or take away from the ones here: whatever extensions are listed on the line after the x-mapp-php5 will be processed via PHP.

Note: On other Linux hosts, you may need to use php5-script instead of x-mapp-php5.

[June 4, 2011 Update: I was setting up my sister's site recently, also on a 1&1 Beginner’s Package, and the style sheets were not working correctly. The fix was to add this line to the .htaccess file immediately above the line shown in the example above:
RemoveHandler .css
I first tried removing all file extensions I was using, including .htm, but this caused problems. Just know that you have the option of removing file handlers and play around with it as needed.

Step 5:

Now test to see whether or not your pages are being compressed by using the free tool from GIDNetwork.

Tuesday, January 20, 2009

How to Compress Perl/CGI-Scripts with 1&1 (and Other Shared Hosts)

According to Yahoo’s Best Practices for Speeding up Websites, it is a good idea to compress (gzip) all text-based files, including php, css, javascript, and the topic of today: Perl CGI scripts. However, if you are hosting your website on 1&1 (or other shared hosting package*), you probably know of no easy way to g-zip such files.

May 6, 2012 Update: If you have the ability to ssh into your server as root, skip to step 4 below. By far the fastest and cleanest method is to install cpanm (if it's not already), then turn around and use it to install CGI::WebGzip:
cpan App::cpanminus
cpanm CGI::WebGzip
Any Perl module can be installed using cpanm Module::Name: see Cpan for more.

I first tried the method outlined in the article How To Install Perl Modules On Shared Hosting, but I couldn’t get it to work quite like the author suggested. With a little experimentation, however, I found a way to gzip Perl files on a 1&1 Beginner’s Package.

Step 1:
Download a copy of CGI-WebGzip from CSPAN. Unzip it, and extract WebGzip.pm, the file you need. (If you don’t have the software to unzip tar.gz files, download a copy of the free 7-zip for your operating system, and tell your computer to use the 7-Zip File Manager (7zFM.exe on Windows) to open files with the tar.gz extension. Such files are "zipped twice," so you'll have to unzip the .gz part, then the .tar part before you finally get to your files.)

Step 2:
Open WebGzip.pm and take out take out the CGI:: on the very first line, changing it from:
package CGI::WebGzip;
to:
package WebGzip;
Step 3:
Upload the WebGzip.pm file to the same directory where the Perl CGI-scripts you want to compress live.

Now open your Perl file. On the second line under the Perl shebang (#!/usr/local/bin/perl), add the following:
use lib "$ENV{DOCUMENT_ROOT}/path_to_Perl_scripts"
where you change path_to_Perl_scripts to the directory from root in which they live.

For example, if you have your Perl files stored in a directory called Perl inside another directory called scripts, the line would read use lib "$ENV{DOCUMENT_ROOT}/scripts/Perl". This tells the script to use the library located in such-and-such directory from the root.

Step 4:
Somewhere in your Perl script should be the following line or one very similar:
print "Content-type: text/html\n\n";
which tells the script to print the output to the screen as HTML. Directly above that line, add the following line:

(Update continued: cpanm installers need to use
use CGI::WebGzip
instead, the normative way of importing Perl modules. Otherwise...)
use WebGzip;
print "Content-type: text/html\n\n";
This line tells the script to include the module you just uploaded when it outputs its contents.

Step 5:
First check and make sure that your Perl script loads as expected: it is possible that you left a semi-colon off somewhere or got something a bit out of place. Once that is done, test to see that your file is being compressed using either HTTP Compression Test or GIDZipTest.

If compression is still not working, go back to step four and see if there are any other such lines in the script. If you are not using a 1&1 hosting package, you might also look at the asterisk below.*

* Your server will need to have the Compress::Zlib module installed. It is possible, even if it is not installed, you could use a method similar to the one outlined in this document to include it: you can download it here.

If you’re not sure if you have it installed and you’re not sure how to check, try the directions here and see if it works. If it works, you have Compress::Zlib installed.