AlexV
AlexV

Reputation: 23098

Header use in PHP

Pretty simple question: which one of these two PHP (version 5+) header call is the "best"?

header('Not Modified', true, 304);
header('HTTP/1.1 304 Not Modified');

I'm pretty sure the first one is the most polyvalent one, but just curious if PHP would "fix" the second one if under HTTP 1.0...

Thanks!

Edit: One of these header crashes PHP on my Web host. Follow-up question at: PHP header() call "crashing" script with HTTP 500 error

Upvotes: 5

Views: 5662

Answers (6)

symcbean
symcbean

Reputation: 48357

I think Gumbo's answer is the most sensible so far. However try this:

<?php
header('Gobbledy Gook', true, 304);
?>

If the first string is not a proper header it is discarded. If iy does look like a valid header it is appended to the headers - try this:

<?php
header('Cache-Control: max-age=10', true, 304);
?>

The manual for header() and note the special cases - in general I think its not advisable to rely on such built-in heuristics.

However, I'm guessing that your actually interested in getting the content cached well by proxies/browsers. In most instances, latency is far more of a problem of a problem than bandwidth. Next consider how a browser behaves when cached content is stale - in the absence of updated caching information, it keeps making repeated requests to the server to see if the content is still stale.

I.e. in most cases ignoring the conditional part of requests (or even better stripping them on the webserver) actually improves performance.

Upvotes: 1

Craig Francis
Craig Francis

Reputation: 1935

For future reference the http_response_code() function is coming in 5.4:

http://www.php.net/manual/en/function.http-response-code.php

An alternative is:

if (!function_exists('http_response_code')) {
    function http_response_code($code = NULL) {

        if ($code !== NULL) {

            switch ($code) {
                case 100: $text = 'Continue'; break;
                case 101: $text = 'Switching Protocols'; break;
                case 200: $text = 'OK'; break;
                case 201: $text = 'Created'; break;
                case 202: $text = 'Accepted'; break;
                case 203: $text = 'Non-Authoritative Information'; break;
                case 204: $text = 'No Content'; break;
                case 205: $text = 'Reset Content'; break;
                case 206: $text = 'Partial Content'; break;
                case 300: $text = 'Multiple Choices'; break;
                case 301: $text = 'Moved Permanently'; break;
                case 302: $text = 'Moved Temporarily'; break;
                case 303: $text = 'See Other'; break;
                case 304: $text = 'Not Modified'; break;
                case 305: $text = 'Use Proxy'; break;
                case 400: $text = 'Bad Request'; break;
                case 401: $text = 'Unauthorized'; break;
                case 402: $text = 'Payment Required'; break;
                case 403: $text = 'Forbidden'; break;
                case 404: $text = 'Not Found'; break;
                case 405: $text = 'Method Not Allowed'; break;
                case 406: $text = 'Not Acceptable'; break;
                case 407: $text = 'Proxy Authentication Required'; break;
                case 408: $text = 'Request Time-out'; break;
                case 409: $text = 'Conflict'; break;
                case 410: $text = 'Gone'; break;
                case 411: $text = 'Length Required'; break;
                case 412: $text = 'Precondition Failed'; break;
                case 413: $text = 'Request Entity Too Large'; break;
                case 414: $text = 'Request-URI Too Large'; break;
                case 415: $text = 'Unsupported Media Type'; break;
                case 500: $text = 'Internal Server Error'; break;
                case 501: $text = 'Not Implemented'; break;
                case 502: $text = 'Bad Gateway'; break;
                case 503: $text = 'Service Unavailable'; break;
                case 504: $text = 'Gateway Time-out'; break;
                case 505: $text = 'HTTP Version not supported'; break;
                default:
                    exit('Unknown http status code "' . htmlentities($code) . '"');
                break;
            }

            $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');

            header($protocol . ' ' . $code . ' ' . $text);

            $GLOBALS['http_response_code'] = $code;

        } else {

            $code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);

        }

        return $code;

    }
}

In this example I am using $GLOBALS, but you can use whatever storage mechanism you like... I don't think there is a way to return the current status code:

https://bugs.php.net/bug.php?id=52555

For reference the error codes I got from PHP's source code:

http://lxr.php.net/opengrok/xref/PHP_5_4/sapi/cgi/cgi_main.c#354

And how the current http header is sent, with the variables it uses:

http://lxr.php.net/opengrok/xref/PHP_5_4/main/SAPI.c#856

Upvotes: 2

Gumbo
Gumbo

Reputation: 655239

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');

Upvotes: 9

mr-sk
mr-sk

Reputation: 13407

I would normally go with the second example - however, when recently benchmarking an application using apachebench, we noticed ab hanging often.

After debugging, it was identified that the header in this style:

header('HTTP/1.1 304 Not Modified')

Was the culprit (Yeah, I have no idea) and after changing it to,

header('Not Modified', true, 304);

Believe it or not ab started working. Very strange but something to think about. I'll probably use the second method going forward.

Upvotes: 2

Ben James
Ben James

Reputation: 125157

There are two things about the behaviour of the first header call that are worth pointing out:

  • If you provide the 3rd argument, PHP will ignore the first string argument and send the correct response for the given number. This might make the first method less prone to programmer errors.
  • PHP seems to respond with a HTTP/1.1 response even when the request was made with HTTP/1.0

Upvotes: 4

Adam Hopkinson
Adam Hopkinson

Reputation: 28795

I'd go with the second one, as the http response code argument is only supported >= PHP 4.3.0 (which could affect code portability).

I've done this many times and not come across any client that doesn't support HTTP/1.1, so unless you have a special case I shouldn't think that would ever be a problem.

Upvotes: 2

Related Questions