Http Statuscode-Line is ignored in ISS Server (and maybe other fastCGI servers)
What steps will reproduce the problem?
Set response-code in an IIS Server like this:
\Yii::$app->response->statusCode = 500;
or create any critical php error (e.g. echo 10/0).
What is the expected result?
Expected result is Statuscode 500 in Browser, and HTTP/1.1 500 Internal Server Error on first line of http headers.
What do you get instead?
Statuscode 200 and first header line of HTTP/1.1 200 OK
Additional info
| Q | A |
|---|---|
| Yii version | 2.0.48.1 |
| PHP version | 8.1 |
| Microsoft-IIS version | 8.5 |
| Operating system | Window |
my Researches to find the problem
I discovered, that yii does not return a statuscode 500 on php errors, and made some research. I followed the defined status-code in the response until I found yii\web\Response::sendHeaders().
After more testing I can ensure that the statuscode is correctly passed in this method and correctly passed into the header function. E.g. adding a custom header as debug like this (e.g. in one of your controller actions):
header("X-StatusLine: HTTP/{$this->version} {$statusCode} {$this->statusText}")
Gives the header X-StatusLine: HTTP/1.0 500 Internal Server Error, what makes me sure that a currect formated statusline is created in the sendHeaders() function, but statuscode is not delivered to browser.
After more reseach I discovered the Status-header, and made some tests adding this extra header.
The line
header("Status: {$statusCode} {$this->statusText}");
in Response::sendStatus did not chance anything. But adding the stuscode to the 3. argument of the header function gave the expected result.
Suggestion to fix
Add the line
header("Status: {$statusCode} {$this->statusText}", true, $statusCode);
right after https://github.com/yiisoft/yii2/blob/504a66dae50c43d7bcfd8cd506b0b4ec2f096b2a/framework/web/Response.php#L383
or maybe replace the existing line with
if (php_sapi_name() === 'cgi-fcgi')
header("Status: {$statusCode} {$this->statusText}", true, $statusCode);
else
header("HTTP/{$this->version} {$statusCode} {$this->statusText}");
Reasons
It seems that IIS (and maybe other servers) do not respect the statuscode / message set as HTTP/{Protocol} {code} {message} -header.
Adding the extra header Status: {code} {message} tells this server to chance their statusline line expected.
As far as I could find out, this behavior has something to do with cgi/fastCGI, so I thinkt this is not only an IIS exclusive problem, and could affect other server types too.
I can see that Symfony is sending
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
so it looks like our way. Do you think that your problem might come from misconfiguration of the server?
I can see that Symfony is sending
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);so it looks like our way. Do you think that your problem might come from misconfiguration of the server?
I took lot of time investigation this problem.
Currently it seems that:
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode
is corrent and fine for all "normal" webservers.
But in this strange fast CGI modus there seem to be situations, where you need an additional header of Status: 500 Error-Text that server changes the response-code.
I am not 100% sure if this happens for all servers in fast cgi mode, or only for IIS, or if it is any special configuration in IIS.
Any disadvantages specifying statusCode argument?
Any disadvantages specifying statusCode argument?
I could not find any disadvantages via google ect.
But I found the matching RFC for this header see rfc3875. Seems to be true, that this header only affects cgi based calls.
Maybe we should extend the qury for api type a bit to somthing linke the example on php.net to
$sapi_type = php_sapi_name();
if (substr($sapi_type, 0, 3) == 'cgi') {
header("Status: {$statusCode} {$this->statusText}", true, $statusCode);
else
header("HTTP/{$this->version} {$statusCode} {$this->statusText}");
That makes it covering fast cgi and basic cgi-mode
Yes. Good idea.
Do you have time for a pull request?
Do you have time for a pull request?
I think I can write the code, I am not sure if this is all, or if there is anything more to do in documentations ect.. I would start to make a pr containing the code, and test it in iss and apache, but I need some time, this week is very busy.