Edit report at http://bugs.php.net/bug.php?id=51749&edit=1

 ID:               51749
 Updated by:       m...@php.net
 Reported by:      theimp at iinet dot net dot au
 Summary:          header("Location:") changing HTTP status
-Status:           Open
+Status:           Feedback
 Type:             Bug
 Package:          HTTP related
 Operating System: Debian Lenny
 PHP Version:      5.3.2

 New Comment:

header("Location: http://www.example.com/";, true, 503) works fine here.


Previous Comments:
------------------------------------------------------------------------
[2010-05-07 02:14:50] theimp at iinet dot net dot au

Just to ensure that any solution is properly considered, I have noticed
a counterpoint to my original argument, and likely the reason for the
specific current behavior.



RFC 3875 (The Common Gateway Interface (CGI) Version 1.1) says that "[If
the script returns a Location header field for a Client Redirect
Response] The Status header field MUST be supplied and MUST contain a
status value of 302 'Found', or it MAY contain an extension-code, that
is, another valid status code that means client redirection."



Critical in deciding the scope of this requirement is understanding what
is a "valid status code that means client redirection". Though it could
be assumed that it would be any HTTP 3xx code (the wording
"extension-code" would normally mean exactly that), the spec is not
unambiguous and the 201 response already proves to be an exception. It's
not just web browsers with interactive interfaces that use HTTP; knowing
what to do next can be useful for any failure code, and as 201 proves,
even some success codes.



(Although RFC 3875 is "Informational" by comparison to HTTP, etc., and
attempts at standardizing CGI any further have been stagnant, I would
nevertheless agree that compliance with RFC 3875 is very important where
possible, even above what I consider to be correct handling of the bug
that was supplied).



On the other hand, RFC 3875 also mentions related behaviors that are not
followed as specified. For example, PHP does not unset script-set
headers (whether set before or after the Location header) if the script
finishes without outputting a message body, as required. Nor does it
implement a mechanism for a Local Redirect Response at all (because it
doesn't make a lot of sense, given the misconception of the roles
"server" and "script" that RFC 3875 has).



Actually, much of considering the behavior of PHP with respect to RFC
3875 also requires deciding if php is the "script" or the "server".
Though it seems obvious that PHP is the script per the definitions of
RFC 3875, large chunks of functionality that are the direct
responsibility of the "server" according to RFC 3875 are performed, and
can only realistically be performed, by PHP. Now that I look, I can find
practically no documentation on PHP regarding which of the two (if not
both) PHP considers itself to be; or in fact much of anything related to
RFC 3875, including how PHP interprets it or indeed even if it agrees to
follow it.



To be honest, I would favor a standards-compliant response, if at all
sensible, even if it was onerous. On the other hand, as someone who has
tried very hard to comply with this and other standards, I find RFC 3875
to be maddeningly deficient, primarily by its confusion between what is
the responsibility (and thus functionality) of the script, and what is
the responsibility of the server. Ultimately, I would conclude that the
original bug as I reported it should be fixed in the way that I
suggested, on the grounds that the *intent* of RFC 3875 is being
followed. I arrive at this assertion by virtue of the fact that the
intent of the Location header according to Section 6.2 is clearly to
ensure that the script author is obeyed and thus the client actually
gets redirected when they should; to prevent a scenario where the script
author intends to redirect the client and sends no message body to this
effect, but interaction with the server or other software results in a
non-redirect response being generated and the user being unable to
understand the result. It is far more important to obey the intent of
the script author when they have taken the care to consider and provide
the correct response code for their script, as by all accounts the
response code is the single most important piece of information returned
and, when explicitly set, the intent of the script author cannot be
realized if this cannot be reliably communicated. Also, I think that
permitting the Location header other than for 3xx codes reflects that,
as per the HTML and related specifications, it is not necessarily
required or even desired for the client (web browser or otherwise) to
obey a Location header and follow it without user confirmation (despite
common practice today); that the client will always follow a Location
directive seems to be a key assumption of Section of 6.2/6.3.



Sorry for the essay; I'll summarize:



I think this is a bug. Others (including the PHP programmers) may not.
Whether it is or is not a bug is a matter of opinion, probably according
to interpretation of RFC 3875.



As a resolution to this bug I propose that the PHP engine not change an
explicitly set response code when a Location header is set. This may
violate the letter of RFC 3875 (but then again, it may not, as
interpretation is ambiguous; and full compliance may be impossible in
any case).



For (imperfect) backwards compatibility, PHP could change the response
code to "302 Found" only if it has not yet been changed (ie; is only
implicitly set to "200 OK").



This bug is probably not a security issue. This bug is probably of low
importance.

------------------------------------------------------------------------
[2010-05-05 16:12:19] theimp at iinet dot net dot au

Description:
------------
Please see bug #25044 (http://bugs.php.net/bug.php?id=25044), where this
issue has previously been addressed to some extent.



When this previous bug was fixed, the fix simply involved adding the
exact mentioned codes to an exception list for status response codes
that are not overwritten upon sending a Location header. Now, 201, 301,
303, 305, and 307 do not overwrite the Response code. Nevertheless, all
others still do.



A more permanent fix would be not setting the status for ANY response
code (very similar to the actual fix originally suggested for #25044).
For backwards compatibility, you could set the response code if it has
not already been set at the time that the Location header is set; but it
should never be overwritten if it already has been set.



HTTP Responses 503 and 426 come immediately to mind as additional
reasonable cases for adding a Location header; but in fact, neither RFC
1945 (HTTP/1.0), RFC 2616 (HTTP/1.1), RFC 2817 (Upgrading to TLS Within
HTTP/1.1), nor any other IETF or other relevant standard limits the
Location header to any particular response, other than to recommend
("SHOULD") it for 301, 302, 303, 305 (could be read as "MUST"), 307, and
suggest it for 201 (and "intentionally undefined" by RFC 4918 (HTTP
Extensions for Web Distributed Authoring and Versioning (WebDAV)) for
207). So preventing any status code from having a Location header is
undesirable (however silly it may be for some certain responses). This
would future-proof the code in question against any future changes that
do not involve a mandatory or forbidden Location: field (for which the
current code would most likely require patching anyway).



(To be fair, this is documented behavior, even if it is
non-standards-compliant. Mind you, the documentation is contradictory;
http_response_code apparently "Forces the HTTP response code to the
specified value.", but at the same time, "The second special case is the
"Location:" header. Not only does it send this header back to the
browser, but it also returns a REDIRECT (302) status code to the browser
unless the 201 or a 3xx status code has already been set". It is also
true that most current clients will ignore a Location header for most
non-3xx responses, but that is unimportant.)



I would not consider this issue to have particular security concerns.

Test script:
---------------
header("HTTP/1.1 503 Service Unavailable");

header("Location: http://www.php.net/";);

Expected result:
----------------
HTTP Response:



HTTP/1.1 503 Service Unavailable

Location: http://www.php.net/

Actual result:
--------------
HTTP Response:



HTTP/1.1 302 Found

Location: http://www.php.net/


------------------------------------------------------------------------



-- 
Edit this bug report at http://bugs.php.net/bug.php?id=51749&edit=1

Reply via email to