330

I have a PHP script that needs to make responses with HTTP response codes (status-codes), like HTTP 200 OK, or some 4XX or 5XX code.

How can I do this in PHP?

hakre
  • 193,403
  • 52
  • 435
  • 836
Paulo Coghi
  • 13,724
  • 14
  • 68
  • 90

7 Answers7

563

I just found this question and thought it needs a more comprehensive answer:

As of PHP 5.4 there are three methods to accomplish this:

Assembling the response code on your own (PHP >= 4.0)

The header() function has a special use-case that detects a HTTP response line and lets you replace that with a custom one

header("HTTP/1.1 200 OK");

However, this requires special treatment for (Fast)CGI PHP:

$sapi_type = php_sapi_name();
if (substr($sapi_type, 0, 3) == 'cgi')
    header("Status: 404 Not Found");
else
    header("HTTP/1.1 404 Not Found");

Note: According to the HTTP RFC, the reason phrase can be any custom string (that conforms to the standard), but for the sake of client compatibility I do not recommend putting a random string there.

Note: php_sapi_name() requires PHP 4.0.1

3rd argument to header function (PHP >= 4.3)

There are obviously a few problems when using that first variant. The biggest of which I think is that it is partly parsed by PHP or the web server and poorly documented.

Since 4.3, the header function has a 3rd argument that lets you set the response code somewhat comfortably, but using it requires the first argument to be a non-empty string. Here are two options:

header(':', true, 404);
header('X-PHP-Response-Code: 404', true, 404);

I recommend the 2nd one. The first does work on all browsers I have tested, but some minor browsers or web crawlers may have a problem with a header line that only contains a colon. The header field name in the 2nd. variant is of course not standardized in any way and could be modified, I just chose a hopefully descriptive name.

http_response_code function (PHP >= 5.4)

The http_response_code() function was introduced in PHP 5.4, and it made things a lot easier.

http_response_code(404);

That's all.

Compatibility

Here is a function that I have cooked up when I needed compatibility below 5.4 but wanted the functionality of the "new" http_response_code function. I believe PHP 4.3 is more than enough backwards compatibility, but you never know...

// For 4.3.0 <= PHP <= 5.4.0
if (!function_exists('http_response_code'))
{
    function http_response_code($newcode = NULL)
    {
        static $code = 200;
        if($newcode !== NULL)
        {
            header('X-PHP-Response-Code: '.$newcode, true, $newcode);
            if(!headers_sent())
                $code = $newcode;
        }       
        return $code;
    }
}
dualed
  • 10,262
  • 1
  • 26
  • 29
  • @dualed (1) wouldn't `headers_sent()` always be true right after calling `header()`? (2) ever find anything like http_response_text() in the 5.4 world? At least the old header() can affect the text after the status code. – Bob Stein Aug 18 '13 at 20:39
  • 1
    @BobStein-VisiBone **(1)** `headers_sent()` is true if you can't add any more headers because content was already sent, not if you have added a header. **(2)** Sorry, no. Other languages have better support though – dualed Aug 19 '13 at 05:39
  • Something I do which I haven't seen mentioned is specify a string for the header which doesn't contain a colon `header('none', false, $response);` which results in the desired response code and no extra header. – Perry Feb 14 '14 at 19:23
  • 1
    @Perry the reason why I don't suggest doing this is the same why I don't suggest the colon-only one. PHP may handle this differently throughout versions, as it is not defined what happens with such a "header", it may fail completely - not setting either header or status, or it may add an invalid header (the http 1.1 protocol standard **requires** a colon) – dualed Feb 16 '14 at 10:37
  • to be even more generic, one could use: header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found', true, 404); – Bill'o Feb 28 '14 at 08:24
  • 18
    I spent hours realizing that `http_response_code` (and maybe more generally modifying the header) doesn't work anymore after you `echo` something. Hope it helps. – Neptilo Oct 24 '14 at 18:33
  • Adding a header_remove() (for > php 5.3) would even remove the garbage header. Furthermore, note that the original code should returned in case a new response code is set. – user23127 Jan 02 '15 at 10:43
  • @Josh could you confirm if you meant "**does** work properly" or "**does not** work properly" for PHP-FPM? Grigore Madalin's answer implies you're suggesting there's a bug - but both you and he use **does work** – icc97 Nov 15 '15 at 11:28
  • @icc97: I can confirm that the form: `header('Status: 301 Moved Permanently'); header("Location: $url", true, 301);` **does** send `HTTP/1.1 301 Moved Permanently` and a new location. Works for 404 also (without the location header, obviously) – Josh Nov 15 '15 at 16:14
  • For 404 I am using: `header('Status: 404 Not Found',true,404);` under PHP-FPM – Josh Nov 15 '15 at 16:15
  • 3
    `http_response_code()` cannot be used to invent custom error codes. For example `http_response_code(930)` will cause the apache log file to show 930 correctly, but an error 500 will actually be sent back to the client. Use the `header()` method instead for this admittedly weird use case. – jlh Aug 31 '16 at 07:46
52

Unfortunately I found solutions presented by @dualed have various flaws.

  1. Using substr($sapi_type, 0, 3) == 'cgi' is not enogh to detect fast CGI. When using PHP-FPM FastCGI Process Manager, php_sapi_name() returns fpm not cgi

  2. Fasctcgi and php-fpm expose another bug mentioned by @Josh - using header('X-PHP-Response-Code: 404', true, 404); does work properly under PHP-FPM (FastCGI)

  3. header("HTTP/1.1 404 Not Found"); may fail when the protocol is not HTTP/1.1 (i.e. 'HTTP/1.0'). Current protocol must be detected using $_SERVER['SERVER_PROTOCOL'] (available since PHP 4.1.0)

  4. There are at least 2 cases when calling http_response_code() result in unexpected behaviour:

    • When PHP encounters an HTTP response code it does not understand, PHP will replace the code with one it knows from the same group. For example "521 Web server is down" is replaced by "500 Internal Server Error". Many other uncommon response codes from other groups 2xx, 3xx, 4xx are handled this way.
    • On a server with php-fpm and nginx http_response_code() function MAY change the code as expected but not the message. This may result in a strange "404 OK" header for example. This problem is also mentioned on the PHP website in Richard F.'s user comment .

For your reference here there is the full list of HTTP response status codes (this list includes codes from IETF internet standards as well as other IETF RFCs. Many of them are NOT currently supported by PHP http_response_code function()): http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

You can easily test this bug by calling:

http_response_code(521);

The server will send "500 Internal Server Error" HTTP response code resulting in errors if you have a custom client application expecting additional HTTP codes.


My solution (for all PHP versions since 4.1.0):

$httpStatusCode = 521;
$httpStatusMsg  = 'Web server is down';
$phpSapiName    = substr(php_sapi_name(), 0, 3);
if ($phpSapiName == 'cgi' || $phpSapiName == 'fpm') {
    header('Status: '.$httpStatusCode.' '.$httpStatusMsg);
} else {
    $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
    header($protocol.' '.$httpStatusCode.' '.$httpStatusMsg);
}

Conclusion

http_response_code() implementation does not support all HTTP response codes and may overwrite the specified HTTP response code with another one from the same group.

The new http_response_code() function does not solve all the problems involved but make things worst introducing new bugs.

The "compatibility" solution offered by @dualed does not work as expected, at least under PHP-FPM.

The other solutions offered by @dualed also have various bugs. Fast CGI detection does not handle PHP-FPM. Current protocol must be detected.

Any tests and comments are appreciated.

hakre
  • 193,403
  • 52
  • 435
  • 836
Grigore Madalin
  • 1,195
  • 11
  • 15
35

Since PHP 5.4 you can use http_response_code() for get and set header status code.

Here is an example:

<?php

// Get the current response code and set a new one
var_dump(http_response_code(404));

// Get the new response code
var_dump(http_response_code());
?>

Here is the document of this function in php.net:

http_response_code

President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
Seyed Ali Roshan
  • 1,476
  • 1
  • 18
  • 37
  • In my experience, this is the best answer. – Scruffy Jun 09 '18 at 23:32
  • Why use var_dump() ? – Tomas Gonzalez Jan 24 '20 at 03:24
  • 1
    But why var_dump() instead of echo? Can the result be not suitable for a simple echo? Or even print_r(). var_dump() seems so not adequate for production code... – Tomas Gonzalez Jan 29 '20 at 16:12
  • @TomasGonzalez it is not a big deal, i just wanted to show u whats in it by print everything with var_dump() and ur right they r not important – Seyed Ali Roshan Jan 31 '20 at 18:53
  • Ok, I see. What called my attention is that in the official docs, the example uses var_dump() aswell. So I was curious about the reason for doing so. There could have been something I was missing. https://www.php.net/manual/en/function.http-response-code.php – Tomas Gonzalez Feb 01 '20 at 14:56
  • 1
    @TomasGonzalez If you check the "Return Values" section from the page you just mentioned you will see how "http_response_code" will return values. And for the reason behind using "var_dump" and not something like "echo" you could check how these functions work. "echo" can only get strings as input, var_dump can use anything you give it and outputs not only the variable but also the type of it. – Morteza Sadri Mar 07 '20 at 02:42
13

Add this line before any output of the body, in the event you aren't using output buffering.

header("HTTP/1.1 200 OK");

Replace the message portion ('OK') with the appropriate message, and the status code with your code as appropriate (404, 501, etc)

sparkey0
  • 1,641
  • 1
  • 12
  • 14
7

If you are here because of Wordpress giving 404's when loading the environment, this should fix the problem:

define('WP_USE_THEMES', false);
require('../wp-blog-header.php');
status_header( 200 );
//$wp_query->is_404=false; // if necessary

The problem is due to it sending a Status: 404 Not Found header. You have to override that. This will also work:

define('WP_USE_THEMES', false);
require('../wp-blog-header.php');
header("HTTP/1.1 200 OK");
header("Status: 200 All rosy");
jaggedsoft
  • 3,858
  • 2
  • 33
  • 41
5

With the header function. There is an example in the section on the first parameter it takes.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
4
header("HTTP/1.1 200 OK");
http_response_code(201);
header("Status: 200 All rosy");

http_response_code(200); not work because test alert 404 https://developers.google.com/speed/pagespeed/insights/

alpc
  • 598
  • 3
  • 6