Asked  7 Months ago    Answers:  5   Viewed   29 times

I have cacheable dynamic content made in PHP 5.1.0+. I already send the correct headers (including Last-Modified and ETag) to clients.

I now want my script to be able to answer $_SERVER['HTTP_IF_MODIFIED_SINCE'] and $_SERVER['HTTP_IF_NONE_MATCH'] when present. When the conditions matches, I want to answer a HTTP 304 "Not Modified" to clients.

What are the correct conditions? When exactly I issue a 304 instead of the whole content?

The accepted answer in question How to know when to send a 304 Not Modified response seems to issue this correctly but I have hard times to port that code to PHP 5.

Thank you!



I've always used:

function caching_headers ($file, $timestamp) {
    $gmt_mtime = gmdate('r', $timestamp);
    header('ETag: "'.md5($timestamp.$file).'"');
    header('Last-Modified: '.$gmt_mtime);
    header('Cache-Control: public');

    if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
        if ($_SERVER['HTTP_IF_MODIFIED_SINCE'] == $gmt_mtime || str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])) == md5($timestamp.$file)) {
            header('HTTP/1.1 304 Not Modified');

Don't remember whether I wrote it or got it from somewhere else...

I'm normally using it at the top of a file in this way:

caching_headers ($_SERVER['SCRIPT_FILENAME'], filemtime($_SERVER['SCRIPT_FILENAME']));
Wednesday, March 31, 2021
answered 7 Months ago

Testing for identity is always faster, because PHP does not have to Type Juggle to evaluate the comparison. However, I'd say the speed difference is in the realms of nanoseconds and totally neglectable.

Related reading:

  • PHP type comparison tables
  • Type Juggling
Wednesday, March 31, 2021
answered 7 Months ago

I would use this one:

header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified', true, 304);

$_SERVER['SERVER_PROTOCOL'] contains the protocol used in the request like HTTP/1.0 or HTTP/1.1.

Edit    I have to admit that my suggestion is senseless. After a few tests I noticed that if the first parameter is a valid HTTP status line, PHP will use that status line regardless if and what second status code was given with the third parameter. And the second parameter (documentation names it replace) is useless too as there can not be multiple status lines.

So the second and third parameter in this call are just redundant:

header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified', true, 304);

Use just this instead:

header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
Saturday, May 29, 2021
answered 5 Months ago

The solution is to ensure that you are using the mysqlnd driver for php.

How do you know that you're not using mysqlnd?

When viewing php -i, there will be no mention of "mysqlnd". The pdo_mysql section will have something like this:


PDO Driver for MySQL => enabled Client API version => 5.1.72

How do you install it?

Most installation guides for L/A/M/P suggest apt-get install php5-mysql but the native driver for MySQL is installed by a different package: php5-mysqlnd. I found that this was available with the ppa:ondrej/php5-oldstable.

To switch to the new driver (on Ubuntu):

  • Remove the old driver:
    apt-get remove php5-mysql
  • Install the new driver:
    apt-get install php5-mysqlnd
  • Restart apache2:
    service apache2 restart

How do I check that the driver is being used?

Now php -i will mention "mysqlnd" explicitly in the pdo_mysql section:


PDO Driver for MySQL => enabled
Client API version => mysqlnd 5.0.10 - 20111026 - $Id:      e707c415db32080b3752b232487a435ee0372157 $

PDO settings

Ensure that PDO::ATTR_EMULATE_PREPARES is false (check your defaults or set it):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Ensure that PDO::ATTR_STRINGIFY_FETCHES is false (check your defaults or set it):
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);

Returned values

  • Floating-point types (FLOAT, DOUBLE) are returned as PHP floats.
  • Integer types (INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT †) are returned as PHP integers.
  • Fixed-Point types (DECIMAL, NUMERIC) are returned as strings.

† BIGINTs with a value greater than a 64 bit signed int (9223372036854775807) will return as a string (or 32 bits on a 32 bit system)

      public 'integer_col' => int 1
      public 'double_col' => float 1.55
      public 'float_col' => float 1.5
      public 'decimal_col' => string '1.20' (length=4)
      public 'bigint_col' => string '18446744073709551615' (length=20)
Tuesday, June 8, 2021
answered 5 Months ago

Problem not in this page and not in PHP scripts. See Google's suggestions:

The following cacheable resources have a short freshness lifetime. Specify an expiration at least one week in the future for the following resources:

  • (expiration not specified)
  • (expiration not specified)
  • (expiration not specified)
  • (expiration not specified)
  • (expiration not specified)
  • (expiration not specified)
  • (expiration not specified)
  • ...

It means, you should cache your static files.
As I can see, you use Apache. In this case you can use mod_expires

For example, you can add into .htaccess file this lines:

ExpiresActive On
ExpiresDefault "access plus 1 seconds"
ExpiresByType text/html "access plus 1 seconds"
ExpiresByType image/x-icon "access plus 2592000 seconds"
ExpiresByType image/gif "access plus 2592000 seconds"
ExpiresByType image/jpeg "access plus 2592000 seconds"
ExpiresByType image/png "access plus 2592000 seconds"
ExpiresByType text/css "access plus 604800 seconds"
ExpiresByType text/javascript "access plus 86400 seconds"
ExpiresByType application/x-javascript "access plus 86400 seconds"
Wednesday, August 18, 2021
Jamey McElveen
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :