Cache it! Solve PHP Performance Problems Article

In the good old days when building web sites was as easy as knocking up a few HTML pages, the delivery of a web page to a browser was a simple matter of having the web server fetch a file. A site’s visitors would see its small, text-only pages almost immediately, unless they were using particularly slow modems. Once the page was downloaded, the browser would cache it somewhere on the local computer so that, should the page be requested again, after performing a quick check with the server to ensure the page hadn’t been updated, the browser could display the locally cached version. Pages were served as quickly and efficiently as possible, and everyone was happy.

Then dynamic web pages came along and spoiled the party by introducing two problems:

  • When a request for a dynamic web page is received by the server, some intermediate processing must be completed, such as the execution of scripts by the PHP engine. This processing introduces a delay before the web server begins to deliver the output to the browser. This may not be a significant delay where simple PHP scripts are concerned, but for a more complex application, the PHP engine may have a lot of work to do before the page is finally ready for delivery. This extra work results in a noticeable time lag between the user’s requests and the actual display of pages in the browser.
  • A typical web server, such as Apache, uses the time of file modification to inform a web browser of a requested page’s age, allowing the browser to take appropriate caching action. With dynamic web pages, the actual PHP script may change only occasionally; meanwhile, the content it displays, which is often fetched from a database, will change frequently. The web server has no way of discerning updates to the database, so it doesn’t send a last modified date. If the client (that is, the user’s browser) has no indication of how long the data will remain valid, it will take a guess. This is problematic if the browser decides to use a locally cached version of the page which is now out of date, or if the browser decides to request from the server a fresh copy of the page, which actually has no new content, making the request redundant. The web server will always respond with a freshly constructed version of the page, regardless of whether or not the data in the database has actually changed.

To avoid the possibility of a web site visitor viewing out-of-date content, most web developers use a meta tag or HTTP headers to tell the browser never to use a cached version of the page. However, this negates the web browser’s natural ability to cache web pages, and entails some serious disadvantages. For example, the content delivered by a dynamic page may only change once a day, so there’s certainly a benefit to be gained by having the browser cache a page–even if only for 24 hours.

If you’re working with a small PHP application, it’s usually possible to live with both issues. But as your site increases in complexity–and attracts more traffic–you’ll begin to run into performance problems. Both these issues can be solved, however: the first with server-side caching; the second, by taking control of client-side caching from within your application. The exact approach you use to solve these problems will depend on your application, but in this chapter, we’ll consider both PHP and a number of class libraries from PEAR as possible panaceas for your web page woes.

Note that in this chapter’s discussions of caching, we’ll look at only those solutions that can be implemented in PHP. For a more general introduction, the definitive discussion of web caching is represented by Mark Nottingham’s tutorial.

Furthermore, the solutions in this chapter should not be confused with some of the script caching solutions that work on the basis of optimizing and caching compiled PHP scripts, such as Zend Accelerator and ionCube PHP Accelerator.

This chapter is excerpted from The PHP Anthology: 101 Essential Tips, Tricks & Hacks, 2nd Edition. Download this chapter plus two others, covering PDO and Databases, and Access Control, in PDF format to read offline.

How do I prevent web browsers from caching a page?

If timely information is crucial to your web site and you wish to prevent out-of-date content from ever being visible, you need to understand how to prevent web browsers–and proxy servers–from caching pages in the first place.

Solutions

There are two possible approaches we could take to solving this problem: using HTML meta tags, and using HTTP headers.

Using HTML Meta Tags

The most basic approach to the prevention of page caching is one that utilizes HTML meta tags:

<meta http-equiv="expires" content="Mon, 26 Jul 1997 05:00:00 GMT"/>
<meta http-equiv="pragma" content="no-cache" />

The insertion of a date that’s already passed into the Expires meta tag tells the browser that the cached copy of the page is always out of date. Upon encountering this tag, the browser usually won’t cache the page. Although the Pragma: no-cache meta tag isn’t guaranteed, it’s a fairly well-supported convention that most web browsers follow. However, the two issues associated with this approach, which we’ll discuss below, may prompt you to look at the alternative solution.

Using HTTP Headers

A better approach is to use the HTTP protocol itself, with the help of PHP’s header function, to produce the equivalent of the two HTML meta tags above:

<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Pragma: no-cache');
?>

We can go one step further than this, using the Cache-Control header that’s supported by HTTP 1.1-capable browsers:

<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', FALSE);
header('Pragma: no-cache');
?>

For a precise description of HTTP 1.1 Cache-Control headers, have a look at the W3C’s HTTP 1.1 RFC. Another great source of information about HTTP headers, which can be applied readily to PHP, is mod_perl’s documentation on issuing correct headers.

Discussion

Using the Expires meta tag sounds like a good approach, but two problems are associated with it:

  • The browser first has to download the page in order to read the meta tags. If a tag wasn’t present when the page was first requested by a browser, the browser will remain blissfully ignorant and keep its cached copy of the original.
  • Proxy servers that cache web pages, such as those common to ISPs, generally won’t read the HTML documents themselves. A web browser might know that it shouldn’t cache the page, but the proxy server between the browser and the web server probably doesn’t–it will continue to deliver the same out-of-date page to the client.

On the other hand, using the HTTP protocol to prevent page caching essentially guarantees that no web browser or intervening proxy server will cache the page, so visitors will always receive the latest content. In fact, the first header should accomplish this on its own; this is the best way to ensure a page is not cached. The Cache-Control and Pragma headers are added for some degree of insurance. Although they don’t work on all browsers or proxies, the Cache-Control and Pragma headers will catch some cases in which the Expires header doesn’t work as intended–if the client computer’s date is set incorrectly, for example.

Of course, to disallow caching entirely introduces the problems we discussed at the start of this chapter: it negates the web browser’s natural ability to cache pages, and can create unnecessary overhead, as new versions of pages are always requested, even though those pages may not have been updated since the browser’s last request. We’ll look at the solution to these issues in just a moment.

How do I control client-side caching?

We addressed the task of disabling client-side caching in “How do I prevent web browsers from caching a page?”, but disabling the cache is rarely the only (or best) option.

Here we’ll look at a mechanism that allows us to take advantage of client-side caches in a way that can be controlled from within a PHP script.

Apache Required!
This approach will only work if you’re running PHP as an Apache web server module, because it requires use of the function getallheaders–which only works with Apache–to fetch the HTTP headers sent by a web browser.

Solutions

In controlling client-side caching you have two alternatives. You can set a date on which the page will expire, or respond to the browser’s request headers. Let’s see how each of these tactics is executed.

Setting a Page Expiry Header

The header that’s easiest to implement is the Expires header–we use it to set a date on which the page will expire, and until that time, web browsers are allowed to use a cached version of the page. Here’s an example of this header at work:

expires.php (excerpt)

<?php
function setExpires($expires) {
header(
‘Expires: ‘.gmdate(‘D, d M Y H:i:s’, time()+$expires).’GMT’);
}
setExpires(10);
echo ( ‘This page will self destruct in 10 seconds<br />’ );
echo ( ‘The GMT is now ‘.gmdate(‘H:i:s’).'<br />’ );
echo ( ‘<a href=”‘.$_SERVER[‘PHP_SELF’].'”>View Again</a><br />’ );
?>

In this example, we created a custom function called setExpires that sets the HTTP Expires header to a point in the future, defined in seconds. The output of the above example shows the current time in GMT, and provides a link that allows us to view the page again. If we follow this link, we’ll notice the time updates only once every ten seconds. If you like, you can also experiment by using your browser’s Refresh button to tell the browser to refresh the cache, and watching what happens to the displayed date.

Go to page: 1 | 2 | 3 | 4 | 5

Replay

Category: programming Time: 2007-11-07 Views: 1
Tags:

Related post

  • Tracing and Correcting PHP Performance Problems 2011-05-25

    I am running a small LAMP-based web server on which PHP-based pages seem to take a minimum of 5 seconds to render. I believe that the problem is some issue with my PHP configuration in particular because: requests for static pages take 0.5s or less t

  • How to solve performance problems with VMware ESXi 5.5 on old Dell PowerEdge 2900 hardware 2014-11-12

    I have a couple of old Dell PowerEdge 2900 servers that have come free and I would like to use them to host a mail server cluster. They are a bit old, but have been running workhorses for a while, and we have a 3rd one that we can use for spare parts

  • Performance Problem: Delay on first request 2012-07-05

    I put together a D7 site with a Minelli subtheme. Along the way I experimented a lot with different themes, different modules. Somewhere along the way I developed an odd performance issue, and now I don't really know what theme/module/config caused i

  • Severe write performance problem 2009-04-30

    [Sorry, I tried to stay short but it just was not possible] I'm running Linux 2.6.22.19 with linux-vserver 2.3.0.34 on a Fujitsu Siemens Primergy RX200 S3 for development purpose. Intel(R) Xeon(R) 5140 @ 2.33Ghz with 4GB RAM (where mostly 500MB are s

  • WordPress MySQL & PHP Performance 2011-04-07

    have a website (www.americanbankingnews.com) that gets 40,000-50,000 page views today. It's currently sitting on a dedicated quad-core Xeon server with 8GB of ram. The site is powered by WordPress and MySQL (sitting on the same server) and I'm curren

  • How can you know what is w3wp.exe doing? (or how to diagnose a performance problem) 2010-10-27

    I'm having a performance problem in a site we've made, and I'm not exactly sure how to start diagnosing it. The short description is: We have a very small site (http://hearablog.com) with very little traffic, in a crappy dedicated server, CPU is alwa

  • Performance problem with first load 2011-01-27

    I'm just trying to create my blog in WordPress hosted on my GoDaddy account (Windows Deluxe plan). WordPress is installed as subsite. When I check my blog after several hours of inactivity I have to wait more than 10 seconds before site opens. I chec

  • Disk usage on IIS, PHP5, performance problems 2011-02-04

    I'm quite worried with a performance problem that I'm facing in one of our production servers. I'm working for a hosting company, so you can imagine how heterogeneous the applications runnning here are. All started with a call of a client complaining

  • GNU/Linux, IO performance problems 2011-11-03

    I have some problems with my system performance. As I was adviced here, I used sar: $sar -u 5 and found out that the problems are caused by a low HDD performance, as I supposed (high %iowait). My disk is Western Digital AV-25, WDC WD5000BUDT-63G8FY0.

  • How did you pick up the art of solving real life problems? Can you shortcut the process? 2011-12-03

    I have been coding on and off on C and to a certain extent on C++ since my college days(2003), but I never had the opportunity to work with a hard core programming responsibility, which could have taught me "industrial grade coding" better and w

  • Will a SSD cache increase Native ZFS performance for me? 2012-02-07

    I'm using Ubuntu 11.10 Desktop x64 with Native ZFS using a mirrored pool of 2x2 TB 6.0 Gbps hard drives. My issue is that I'm only getting about 30 Mb/s read/write at any time, I would think my system could perform faster. There are some limitations

  • OpenGL GLX context is not using direct rendering, which may cause performance problems. (steam) AMD DRIVER 2014-08-03

    When I start steam in a terminal, I get the following error messages [email protected]:~$ steam rm: no se puede borrar «/home/charlie/.steam/steam»: Es un directorio rm: no se puede borrar «/home/charlie/.steam/bin»: Es un directorio Running Steam on

  • MySQL performance problems on particular vhosts 2014-08-09

    I have a dedicated server setup (8 cores, 16 GB of RAM), running Ubuntu 12.04 LTS with latest updates, and I am experiencing some performance problems related to MySQL. I have multiple vhosts active. Did some basic performance tuning. See below. Prob

  • MySQL performance problem using indexed datetime column 2014-08-10

    I tried to solve the following problem for about one hour now and still didn't get any further with it. Okay, I have a table (MyISAM): +---------+-------------+------+-----+-------------------+----------------+ | Field | Type | Null | Key | Default |

  • SQL Performance Problem IA64 2009-12-09

    We've got a performance problem in production. QA and DEV environments are 2 instances on the same physical server: Windows 2003 Enterprise SP2, 32 GB RAM, 1 Quad 3.5 GHz Intel Xeon X5270 (4 cores x64), SQL 2005 SP3 (9.0.4262), SAN Drives Prod: Windo

  • Explain this strange environment-related Ruby performance problem 2009-12-16

    We're diagnosing Ruby performance problems on our application servers which we've managed to reduce to a simple test case. We compare performance on a machine in our development cluster to a machine in our production data centre. We used this simple

  • Performance problems on Ubuntu guest on VMWare player 2010-07-22

    I'm looking for some advice on how to troubleshoot performance problems. Here's the situation: Windows Server 2008 host, Ubuntu 9.04 Server guest on VMWare Player 3. Host hardware is a Celeron 2GHz with 2GB RAM; Guest has 256MB RAM. The server is use

  • Performance problem on a VMWare ESX virtual hard drive 2010-10-07

    I have performance problems on a virtual hard drive on a VMWare ESX 4.0 virtual machine. See the weird following sequential read speed graph: I passed the test several times, and the shape is consistent. The guest OS is Windows Server 2003 Standard E

  • Troubleshooting ArcObjects performance problems 2011-02-16

    We have developed a relatively complex geoprocessing routine using a C# / ArcObjects (COM) / ArcGIS Server 9.3.1 / ArcSDE 9.3.1 / Oracle 11g stack. Running the routine on a complete dataset takes many, many days and often does not complete. Troublesh

iOS development

Android development

Python development

JAVA development

Development language

PHP development

Ruby development

search

Front-end development

Database

development tools

Open Platform

Javascript development

.NET development

cloud computing

server

Copyright (C) avrocks.com, All Rights Reserved.

processed in 1.807 (s). 13 q(s)