php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #76331 Location header overrides Content-Type
Submitted: 2018-05-11 16:09 UTC Modified: 2019-09-13 13:47 UTC
From: Andy_Schmidt at HM-Software dot com Assigned: cmb (profile)
Status: Not a bug Package: IIS related
PHP Version: 7.2.5 OS: Win 2012
Private report: No CVE-ID: None
 [2018-05-11 16:09 UTC] Andy_Schmidt at HM-Software dot com
Description:
------------
Setting the "Location" header will set HTTP Status to 302 by default - but ONLY if no explicit Status was set. This is a helpful/reasonable automatism.

However, it will also FORCE a Content-Type of "text/html; charset=UTF-8" and adding 224 bytes to the content, disregarding any explicit Content-Type that was set (no matter if set prior or after setting "Location").

HTTP RFCs only state that the content of a redirect "SHOULD" include a forwarding link - but does NOT insist on any Content-Type, nor is this a "MUST".

Every worse, PHP even overrides perfectly valid (standards-compliant) "HTML" content-types, such as "application/xhtml+xml".


I believe, similar how PHP honors any explicitly set status code, it should also honor any explicitly-set Content-Type and optional supplied content, and only revert to its default Content-Type and content, if NO Content-Type was set.


Test script:
---------------
http_response_code( 307 );
header( 'Content-Type: application/xhtml+xml' );
header( 'Location: /transient/media/3315-234_a438fc6e0bd719703694e7bfc0b1392ecbb7a6a6_S-2.jpeg' );
header( 'Content-Type: application/xhtml+xml' );
exit;

Expected result:
----------------
Response headers should be:

...
Content-Length: 0
Content-Type: application/xhtml+xml
...

Actual result:
--------------
Response headers are:

...
Content-Length: 224
Content-Type: text/html; charset=UTF-8
...

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-05-11 16:40 UTC] cmb@php.net
-Status: Open +Status: Feedback -Assigned To: +Assigned To: cmb
 [2018-05-11 16:40 UTC] cmb@php.net
This appears to be SAPI related, since I get the specified
Content-Type header.  Which SAPI do you use?

Also please check whether it makes a difference, if you set the
$http_response_code via header() instead of using
http_response_code() explicitly.
 [2018-05-11 19:37 UTC] Andy_Schmidt at HM-Software dot com
-Status: Feedback +Status: Assignedǡ
 [2018-05-11 19:37 UTC] Andy_Schmidt at HM-Software dot com
SAPI is standard IIS 8.5 FastCGI.


Using:
header( 'Location: /transient/media/3315-234_a438fc6e0bd719703694e7bfc0b1392ecbb7a6a6_S-2.jpeg', FALSE, 307 ); 
did NOT alter the outcome.

Also, disabling XDEBUG did not have any effect.
 [2018-05-11 20:21 UTC] spam2 at rhsoft dot net
WTF - a redirect don't need whatever content-type because it should not contain any http body at all
 [2018-05-11 20:30 UTC] Andy_Schmidt at HM-Software dot com
-Status: Assignedǡ +Status: Open
 [2018-05-11 20:30 UTC] Andy_Schmidt at HM-Software dot com
>> spam2@rhsoft.net: ...should not contain any http body at all <<

I respect your opinion, however, this is governed by RFCs, which states precisely the OPPOSITE: 
"Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s)." (https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)

This allows for clients that do NOT perform an automatic redirect. In fact there are scenarios (e.g., request was not "GET" or "HEAD") where the automatic redirect MUST NO occur!
 [2018-05-11 20:34 UTC] Andy_Schmidt at HM-Software dot com
PS: It is also possible to use redirect status 301/302/307 and NOT include a "Location: " header. I've heard of cases where this is used to indicate that the current URL is no longer valid, but automatic forwarding is NOT wanted. 

Instead, content is displayed that might offer up a number of alternative links. And, of course, that content might be served up in whatever Content-Type DIFFERENT from the "text/html" that currently is being hard-substituted.
 [2018-05-11 20:42 UTC] Andy_Schmidt at HM-Software dot com
>> spam2@rhsoft.net: ...should not contain any http body at all <<

Automatic redirection is NOT a requirement, moreover, inclusion of a payload IS explicitly mentioned as being common - exactly the opposite of your opinion. Please note https://tools.ietf.org/html/rfc7231#section-6.4.2:

"The server SHOULD generate a Location header field in the response
   containing a preferred URI reference for the new permanent URI.  The
   user agent MAY use the Location field value for automatic
   redirection.  The server's response payload usually contains a short
   hypertext note with a hyperlink to the new URI(s)."
 [2018-05-11 20:55 UTC] cmb@php.net
-Status: Assigned +Status: Open -Assigned To: cmb +Assigned To:
 [2018-05-11 20:55 UTC] cmb@php.net
> SAPI is standard IIS 8.5 FastCGI.

Thanks.  So this is likely an (F)CGI issue (I've tested with
Apache mod_php).
 [2018-05-11 20:57 UTC] spam2 at rhsoft dot net
WTF - especially after a POST request succeeded automatic redirects to a confirmation page are common to avoid multiple submits and one yet need to show me any client not following a http redirect unconditional
 [2018-05-11 21:20 UTC] cmb@php.net
> […] one yet need to show me any client not following a http
> redirect unconditional

<https://curl.haxx.se/> does not even follow redirects by default.

Anyway, this bug tracker is most certainly not the appropriate
place to discuss the reasonableness of Internet standards and
possibly divergent behavior of clients.  Please let's stick to the
issue at hand, which is that the supplied Content-Type header is
overridden for apparently no good reason.
 [2018-06-08 21:28 UTC] Andy_Schmidt at HM-Software dot com
-Status: Open +Status: Openp -Operating System: +Operating System: Win 2012
 [2018-06-08 21:28 UTC] Andy_Schmidt at HM-Software dot com
There are additional complications due to this behavior, in case it helps tracking it down. I just lost a few days tracking down impossible problems in my asynchronous application (claiming that there was additional output after the headers had been written and the buffers had been flush()ed and ob_flushed() )

At the end those turned down to be another side effect of this behavior. Because, NOT ONLY does the system override the "Content-Type", it actually injects a default HTML content with the title "Document Moved", and an H1 of "Object Moved" and a body of "This document may be found here". To make things even worse, it leaves the original "Content-Length" of the image file in place. 

If THAT default HTML redirect content is part of the PHP code, then this would suggest that this behavior is actually native to PHP!

From here things go quickly downhill. Either due to the excessive (= wrong) content length, or due to the previous content type, or maybe PHP still has the original jpeg data stream in a buffer, the FCGI handler will then be handed an extraneous data stream starting with hex FF D8 FF E0 ... which is the JPEG filetype signature. The extra data (after the request had supposedly already completed) causes the web server/FCGI to log a system error and terminate the instance.
 [2018-06-09 06:54 UTC] requinix@php.net
-Status: Openp +Status: Open
 [2019-09-12 09:01 UTC] cmb@php.net
-Status: Open +Status: Feedback -Package: HTTP related +Package: IIS related -Assigned To: +Assigned To: cmb
 [2019-09-12 09:01 UTC] cmb@php.net
I cannot reproduce this with IIS 10 FCGI (PHP 7.2.22).

> Because, NOT ONLY does the system override the "Content-Type",
> it actually injects a default HTML content with the title
> "Document Moved", and an H1 of "Object Moved" and a body of "This
> document may be found here".

This sounds like a Webserver configuration issue.  Is there,
maybe, an error page defined for 307?
 [2019-09-12 23:48 UTC] Andy_Schmidt at HM-Software dot com
No error page for 307 configured at all.
Sorry, have no IIS 10 to test.

Just reran my test with 7.3.8 under IIS 8.5 - tried both with FastCGI and even had it run native CGI through php-cgi-.exe.  I inspected the raw headers in Firefox' Network Tools to confirm that both the Content-Type and Content-Length is "altered".

HTTP/1.1 307 Temporary Redirect
Content-Type: text/html; charset=UTF-8
Location: /62044-206/9edf47546f10b3f4d0b2e9420c5d74f5f2343d53/D.jpeg
Server: Microsoft-IIS/8.5
X-Powered-By: PHP/7.3.8
X-Powered-By: ASP.NET
Date: Thu, 12 Sep 2019 23:38:27 GMT
Content-Length: 231
 [2019-09-13 00:14 UTC] Andy_Schmidt at HM-Software dot com
-Status: Feedback +Status: Assigned
 [2019-09-13 00:14 UTC] Andy_Schmidt at HM-Software dot com
PS - if I intentionally "garble" the "Location:" header, THEN the Content-Type and Content-Length headers remain unaltered:

HTTP/1.1 307 Temporary Redirect
Content-Type: application/xhtml+xml
Server: Microsoft-IIS/8.5
X-Powered-By: PHP/7.3.8
X-Location: /62044-206/9edf47546f10b3f4d0b2e9420c5d74f5f2343d53/D.jpeg
X-Powered-By: ASP.NET
Date: Fri, 13 Sep 2019 00:11:01 GMT
Content-Length: 0

Naturally, it won't actually redirect, for lack of a new location, but at least it demonstrates that the issue is not based on some "configuration" detail, but rather triggered by the existence of a "Location:" request header.
 [2019-09-13 10:17 UTC] cmb@php.net
-Status: Assigned +Status: Not a bug
 [2019-09-13 10:17 UTC] cmb@php.net
Well, it seems that this is a known issue that affects older IIS
versions[1], and I don't think we can do anything about it.

[1] <https://forums.iis.net/t/1209573.aspx#2082988>
 [2019-09-13 13:16 UTC] Andy_Schmidt at HM-Software dot com
Indeed, I do suspect the same (I had come across that reference as well last night so I did some targeted testing). However, it actually does NOT seem to be limited to FastCGI, it even happens if I configure IIS to process .PHP through IIS' standard CGI interface to "php-cgi.exe", rather than IIS' FastCGI ISAPI.

I'd be interested to see the headers that Win 2016 generated for my reference.

But I do agree that the injected HTML content, type and length appears to be injected by IIS (possibly up to 2012), not PHP.
 [2019-09-13 13:47 UTC] cmb@php.net
On Windows 10 I get (regardless whether the location resource
exists or not):

HTTP/1.1 307 Temporary Redirect
Content-Type: application/xhtml+xml
Location: /cherry.jpg
Server: Microsoft-IIS/10.0
X-Powered-By: PHP/7.2.22
Date: Fri, 13 Sep 2019 13:41:05 GMT
Content-Length: 0
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Dec 08 03:01:28 2024 UTC