# [PATCH] Allow unsetting headers previously set using header()

- Thread: https://externals.io/message/41281
- Original archive: https://news-web.php.net/php.internals/41281
- Started by Christian Schmidt on 2008-10-19T21:41:27+00:00
- Messages: 14
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #41281 — Christian Schmidt — 2008-10-19T21:41:27+00:00

URL: https://externals.io/message/41281#41281

(This is a repost of a proposal originally sent to the list 6 months 
ago. It didn't gather much attention back then, but I talked to several 
people off-list who found it useful and suggested that I should try to 
bring it up again at a later date - so here goes)

When a header has been set using header('Foo: bar') it can be replaced 
with another value, but it cannot be removed.

This ability to remove headers is relevant e.g. if the auto_prepend_file 
calls header('Vary: Accept-Language'), but later on in the request it is 
determined that a particular file does not do language-negotiation (e.g. 
a PHP script outputting an image file). Setting it to the empty string 
using header('Vary:') is not equivalent to unsetting the header.

It is possible to work around this limitation by maintaining a custom 
header buffer, but since PHP allows replacing and appending to 
previously set headers, it seems reasonable to also allow unsetting headers.

I suggest extending the behaviour of `header()` so that when the first 
argument does not contain a colon (and does not begin with "HTTP/"), 
e.g. header('Vary'), it unsets the header with the specified name. I 
decided to extend the existing `header()` function rather than adding a 
new function, because the existing function already supports multiple 
actions (add, append and replace).

AFAICT this change is backwards compatible. According to the HTTP spec, 
RFC 2616, section 4.2, all header lines must include a colon, so calling 
e.g. header('Vary') does currently not generate a valid header:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2

The attached patch contains an implementation for the cgi and 
apache2handler SAPIs for PHP_5_3. The 6 months old patch still applies, 
though I haven't tested it again except from running "make test".

AFAICT these SAPIs don't need to be changed:
cli, embed, isapi, milter, pi3web, thttpd, tux, webjames.

These SAPIs need additional patching:
apache, apache_hooks, apache2filter, continuity/capi, caudium, nsapi, 
phttpd, roxen
I don't have an easy way to test these SAPIs, so any help with adding 
support for them is appreciated. AFAICT some of these don't have full 
 `header()` support (in particular for the second $replace parameter), so 
it may not be a big issue if they aren't patched right away.

For some reason I couldn't build apache2filter on my machine.

The attached phpt file is intended to be put in 
ext/standard/tests/general_functions.


Christian

---

## #41283 — Hannes Magnusson — 2008-10-20T07:12:07+00:00

URL: https://externals.io/message/41281#41283 · In reply to: #41281

> When a header has been set using header('Foo: bar') it can be replaced with
> another value, but it cannot be removed.

Hah! Hit that one last Friday too, gosh that was annoying!

> I suggest extending the behaviour of `header()` so that when the first
> argument does not contain a colon (and does not begin with "HTTP/"), e.g.
> header('Vary'), it unsets the header with the specified name. I decided to
> extend the existing `header()` function rather than adding a new function,
> because the existing function already supports multiple actions (add, append
> and replace).


That isn't very intuitive. I would think it was a typo when reading
such code and fix the header line...
I'd suggest explicit header_remove("Vary");

-Hannes

---

## #41318 — johannes@schlueters.de — 2008-10-22T11:40:40+00:00

URL: https://externals.io/message/41281#41318 · In reply to: #41283

Hi,


> > […]
> 
> 
> That isn't very intuitive. I would think it was a typo when reading
> such code and fix the header line...
> I'd suggest explicit header_remove("Vary");

Agreed, a specific function with a clear name sounds better.

Christian, can you update the patch? - thanks!

johannes

---

## #41319 — Stan Vassilev | FM — 2008-10-22T12:02:30+00:00

URL: https://externals.io/message/41281#41319 · In reply to: #41318

> > […]
>
> Agreed, a specific function with a clear name sounds better.
>
> Christian, can you update the patch? - thanks!
>
> johannes

I suggest header_remove('*') or simply `header_remove()` /no param/ removes 
all headers (including the one PHP sets by default), so we can start with a 
clear state.

Regards,
Stan Vassilev

---

## #41782 — Christian Schmidt — 2008-11-09T18:51:31+00:00

URL: https://externals.io/message/41281#41782 · In reply to: #41319

Stan Vassilev | FM wrote:
> I suggest header_remove('*') or simply `header_remove()` /no param/ 
> removes all headers (including the one PHP sets by default), so we can 
> start with a clear state.

I added header_remove('Foo'). `header_remove()` without arguments removes 
all headers (though Apache still adds some headers that you cannot remove).

I have tested with apache2handler and cgi. I had to change the signature 
of SAPI header_handler function and sapi_header_struct, so the other 
SAPIs should be updated for this. I am not sure how to test all these? 
Creating a testing environment for all those webservers seems like a 
huge task.

I am not comfortable with the size of this patch, given my understanding 
of the PHP source code and my general C skills, so I am posting this 
patch hoping that somebody will pick it up or help me get it into shape.


Christian

---

## #41785 — Arnaud Le Blanc — 2008-11-09T21:01:36+00:00

URL: https://externals.io/message/41281#41785 · In reply to: #41782

On Sunday 09 November 2008 19:51:31 Christian Schmidt wrote:
> Stan Vassilev | FM wrote:
> > […]
> 
> I added header_remove('Foo'). `header_remove()` without arguments removes 
> all headers (though Apache still adds some headers that you cannot remove).
> 
> I have tested with apache2handler and cgi. I had to change the signature 
> of SAPI header_handler function and sapi_header_struct, so the other 
> SAPIs should be updated for this. I am not sure how to test all these? 
> Creating a testing environment for all those webservers seems like a 
> huge task.
> 
> I am not comfortable with the size of this patch, given my understanding 
> of the PHP source code and my general C skills, so I am posting this 
> patch hoping that somebody will pick it up or help me get it into shape.
> 

It looks good. The signature change is not that bad if it forces all SAPIs to 
be updated and ensures that PHP behaves the same way with all SAPIs.

It is also possible to add something like header_delete_handler() or 
header_handler_ex() to sapi_module_struct if the signature change is to be 
avoided.

Regards,

Arnaud

---

## #41787 — Uwe Schindler — 2008-11-09T21:49:47+00:00

URL: https://externals.io/message/41281#41787 · In reply to: #41785

+1

I have no problem with implementing this for NSAPI after the patch is
committed to CVS, just keep me informed about this.

-----
Uwe Schindler
thetaphi@php.net - http://www.php.net
NSAPI SAPI developer
Bremen, Germany

> -----Original Message-----
> From: Arnaud LB [mailto:arnaud.lb@gmail.com] On Behalf Of Arnaud Le Blanc
> Sent: Sunday, November 09, 2008 10:02 PM
> To: internals@lists.php.net
> Cc: Christian Schmidt
> Subject: Re: [PHP-DEV] [PATCH] Allow unsetting headers previously set
> usingheader()
> 
> On Sunday 09 November 2008 19:51:31 Christian Schmidt wrote:
> > […]
> remove).
> > […]
> 
> It looks good. The signature change is not that bad if it forces all SAPIs
> to
> be updated and ensures that PHP behaves the same way with all SAPIs.
> 
> It is also possible to add something like header_delete_handler() or
> header_handler_ex() to sapi_module_struct if the signature change is to be
> avoided.
> 
> Regards,
> 
> Arnaud

---

## #41893 — Arnaud Le Blanc — 2008-11-13T13:48:30+00:00

URL: https://externals.io/message/41281#41893 · In reply to: #41787

Hi,

Committed, thanks Christian :)

apache2handler, apache2filter, apache, apache_hooks, cli and cgi SAPIs have 
been updated.

The following SAPIs need to be updated in PHP_5_3 and HEAD:
aolserver, continuity, litespeed, nsapi, caudium, phttpd, roxen. (I'm CC-ing 
known maintainers)

More informations on the change can be found in the commit message:
http://news.php.net/php.cvs/54228

Regards,

Arnaud

On Sunday 09 November 2008 22:49:47 Uwe Schindler wrote:
> +1
> 
> I have no problem with implementing this for NSAPI after the patch is
> committed to CVS, just keep me informed about this.
> 
> -----
> Uwe Schindler
> thetaphi@php.net - http://www.php.net
> NSAPI SAPI developer
> Bremen, Germany
> 
> > […]

---

## #42068 — Lukas Kahwe Smith — 2008-11-28T13:06:37+00:00

URL: https://externals.io/message/41281#42068 · In reply to: #41893

> Hi,
>
> Committed, thanks Christian :)
>
> apache2handler, apache2filter, apache, apache_hooks, cli and cgi  
> SAPIs have
> been updated.
>
> The following SAPIs need to be updated in PHP_5_3 and HEAD:
> aolserver, continuity, litespeed, nsapi, caudium, phttpd, roxen.  
> (I'm CC-ing
> known maintainers)
>
> More informations on the change can be found in the commit message:
> http://news.php.net/php.cvs/54228


err .. whats the status here?

regards,
Lukas Kahwe Smith
mls@pooteeweet.org

---

## #42069 — Uwe Schindler — 2008-11-28T13:17:03+00:00

URL: https://externals.io/message/41281#42069 · In reply to: #42068

Still working on it, hadn't have enough time for it until now, I try to do
it as soon as possible!

-----
Uwe Schindler
thetaphi@php.net - http://www.php.net
NSAPI SAPI developer
Bremen, Germany

> From: Lukas Kahwe Smith [mailto:mls@pooteeweet.org]
> Sent: Friday, November 28, 2008 2:07 PM
> To: Arnaud Le Blanc
> Cc: Uwe Schindler; internals@lists.php.net; 'Christian Schmidt'; Alex
> Leigh; George Wang
> Subject: Re: [PHP-DEV] [PATCH] Allow unsetting headers previously set
> usingheader()
> 
> 
> 
> 
> > […]
> 
> 
> err .. whats the status here?
> 
> regards,
> Lukas Kahwe Smith
> mls@pooteeweet.org

---

## #42073 — Uwe Schindler — 2008-11-28T17:24:38+00:00

URL: https://externals.io/message/41281#42073 · In reply to: #42068

Just one question here:
When implementing this into NSAPI, I found the following problem:

NSAPI does not directly allows to remove all headers, you can only do this
step by step. So there are three possibilities to ship around this problem:

a) when SAPI_HEADER_DELETE_ALL is given, in header_handler, do the following
(using the sapi_header_struct given as last parameter):

zend_llist_apply(sapi_headers->headers, (llist_apply_func_t)
php_nsapi_remove_header TSRMLS_CC);

with:
static int php_nsapi_remove_header(sapi_header_struct *sapi_header
TSRMLS_DC)
{
	char *header_name, *p;
	nsapi_request_context *rc = (nsapi_request_context
*)SG(server_context);
	
	header_name = nsapi_strdup(sapi_header->header);
	if (p = strchr(header_name, ':')) *p = 0;

	...

	param_free(pblock_remove(header_name, rc->rq->srvhdrs));
	nsapi_free(header_name);
	
	return ZEND_HASH_APPLY_KEEP;
}

This would remove all headers, set by PHP. Headers embedded by the server
itself would not be deleted (e.g. "Server:"  etc.)

b) Use some "hack" to get rid of all headers from the server's hashtable
(like apr_table_clear()). This would remove all headers and also some
important headers set by the server itself (like Server:, Chunked response
headers, etc). I am not sure if this would be good, in my opinion
SAPI_HEADER_DELETE_ALL should only remove headers set by PHP itself! What
does the other SAPI developers think, is it safe to remove apaches default
headers????? I am not sure and tend to say: NO!

c) Completely ignore sapi_header_handler like in CGI and set the headers
later in sapi_send_headers() using the given struct with zend_llist_apply().
Then I do not have to take care of adding/removing headers, I set them to
Sun Webserver shortly before starting the response. Why the difference
between header_handler and send_headers? Is it ok to remove header_handler
(like in CGI) and simply feed all headers in send_headers? This would make
life for SAPI developers easier (also maybe in Apache). What is the idea to
respond after each header change?

What do you think?

-----
Uwe Schindler
thetaphi@php.net - http://www.php.net
NSAPI SAPI developer
Bremen, Germany


> -----Original Message-----
> From: Lukas Kahwe Smith [mailto:mls@pooteeweet.org]
> Sent: Friday, November 28, 2008 2:07 PM
> To: Arnaud Le Blanc
> Cc: Uwe Schindler; internals@lists.php.net; 'Christian Schmidt'; Alex
> Leigh; George Wang
> Subject: Re: [PHP-DEV] [PATCH] Allow unsetting headers previously set
> usingheader()
> 
> 
> 
> 
> > […]
> 
> 
> err .. whats the status here?
> 
> regards,
> Lukas Kahwe Smith
> mls@pooteeweet.org

---

## #42074 — Arnaud Le Blanc — 2008-11-29T02:02:31+00:00

URL: https://externals.io/message/41281#42074 · In reply to: #42073

Hi,


On Friday 28 November 2008 18:24:38 Uwe Schindler wrote:
> Just one question here:
> When implementing this into NSAPI, I found the following problem:
> 
> NSAPI does not directly allows to remove all headers, you can only do this
> step by step. So there are three possibilities to ship around this problem:
> 
> a) when SAPI_HEADER_DELETE_ALL is given, in header_handler, do the following
> (using the sapi_header_struct given as last parameter):
> 
> zend_llist_apply(sapi_headers->headers, (llist_apply_func_t)
> php_nsapi_remove_header TSRMLS_CC);
> 
> with:
> static int php_nsapi_remove_header(sapi_header_struct *sapi_header
> TSRMLS_DC)
> {
> 	char *header_name, *p;
> 	nsapi_request_context *rc = (nsapi_request_context
> *)SG(server_context);
> 	
> 	header_name = nsapi_strdup(sapi_header->header);
> 	if (p = strchr(header_name, ':')) *p = 0;
> 
> 	...
> 
> 	param_free(pblock_remove(header_name, rc->rq->srvhdrs));
> 	nsapi_free(header_name);
> 	
> 	return ZEND_HASH_APPLY_KEEP;
> }
> 
> This would remove all headers, set by PHP. Headers embedded by the server
> itself would not be deleted (e.g. "Server:"  etc.)
> 
> b) Use some "hack" to get rid of all headers from the server's hashtable
> (like apr_table_clear()). This would remove all headers and also some
> important headers set by the server itself (like Server:, Chunked response
> headers, etc). I am not sure if this would be good, in my opinion
> SAPI_HEADER_DELETE_ALL should only remove headers set by PHP itself! What
> does the other SAPI developers think, is it safe to remove apaches default
> headers????? I am not sure and tend to say: NO!
> 
> c) Completely ignore sapi_header_handler like in CGI and set the headers
> later in sapi_send_headers() using the given struct with zend_llist_apply().
> Then I do not have to take care of adding/removing headers, I set them to
> Sun Webserver shortly before starting the response. Why the difference
> between header_handler and send_headers? Is it ok to remove header_handler
> (like in CGI) and simply feed all headers in send_headers? This would make
> life for SAPI developers easier (also maybe in Apache). What is the idea to
> respond after each header change?

I believe that Apache does sets its headers just before sending them, so when 
PHP deletes all headers in Apache's hashtable this does not removes "Server", 
"Date", etc. If this is not the case for NSAPI, solution a) seems good, but 
also allows to remove "Date" by setting it before ;)

One reason for having header_handler() and send_headers() is for example that 
 `flush()` does not sends headers explicitly, but lets the server send them based 
on what header_handler() has set.

Regards,

Arnaud

---

## #42077 — Uwe Schindler — 2008-11-29T12:08:36+00:00

URL: https://externals.io/message/41281#42077 · In reply to: #42074

Hallo Arnaud,

> I believe that Apache does sets its headers just before sending them, so
> when
> PHP deletes all headers in Apache's hashtable this does not removes
> "Server",
> "Date", etc. If this is not the case for NSAPI, solution a) seems good,
> but
> also allows to remove "Date" by setting it before ;)

That is correct, you are able to remove the headers. For removing the Date
header it would be enough to just use the `header_remove()` without setting it
before. But if somebody does this, he knows what he is doing. I want to
prevent PHP from doing things that the user cannot control or understand.
Sun Webserver for example is able to compress output automatically and other
things. So I am not sure what happens, if you remove headers. As source code
of this webserver is not yet available (Sun wants to release it soon), I do
not know *when* nsapi puts entries in his internal header hash table. Maybe
its in protocol_start_response(), if so everything would be ok, and I can
manually cleanup the complete webserver's hash table (solution b). But
nsapi_response_headers from PHP called shows, that e.g. Date and Server are
set *before* the request starts.

In my opinion, a call to `header_remove()` from PHP should only remove headers
set by PHP (e.g. revert to the state, before the PHP script started).

> One reason for having header_handler() and send_headers() is for example
> that
> `flush()` does not sends headers explicitly, but lets the server send them
> based
> on what header_handler() has set.

That's not correct for at least apache:

php_apache_sapi_flush(void *server_context)
{
	...
	sapi_send_headers(TSRMLS_C);

	r->status = SG(sapi_headers).http_response_code;
	SG(headers_sent) = 1;

	if (ap_rflush(r) < 0 || r->connection->aborted) {
		php_handle_aborted_connection();
	}
}

The flush function just calls sapi_send_headers so it explicitely send the
headers, so my approach of do not implementing a header_handler would also
correctly send the headers in this case. For NSAPI it is not even possible
to send headers explicitely, you can only set them in an internal hashtable
of the web server.

For a typical NSAPI module the workflow is the following:
Any "Service" handler from webserver's config sets headers in the internal
hash table. Before it starts to write output, it may also set the response
code. All this is not sent to the client immediately. The headers and
everything is sent on protocol_start_response(). After that you cannot set
any headers anymore and you have to write to the output stream.

The PHP module calls the webserver's protocol_start_response() in
sapi_send_headers(), so even when you flush, this must be called (in the
current version of output buffering there is still a bug about this, which
is fixed in the new version, I think). I repaired this by also implementing
flush for nsapi in my new version, which calls like apache
sapi_send_headers(TSRMLS_C). This fixes a very old bug for NSAPI.

So in my opinion for the server it is no difference when to set the headers
(immediately after the call to `header()`) or shortly before starting the
reponse. And this is how CGI works - and this would be enough for NSAPI,
too.

The only thing that would not work is explicitely removing headers like
"Date:", but for this use case header_handler() could only be implemented
for the op SAPI_HEADER_DELETE. But with CGI this would not be possible.

Uwe

---

## #42078 — Uwe Schindler — 2008-11-29T15:00:18+00:00

URL: https://externals.io/message/41281#42078 · In reply to: #42077

I implemented and tested now version a), works fine! `header_remove()` now
only removes headers set/modified by PHP.

I tested the case to remove *all* headers by cleaning the server's header
hash table, but this breaks server hard: 

Best example: Removing the "Transfer-encoding: chunked" header, the server
inserts at the beginning of the request before PHP is executed, is not
restored on protocol_start_response, but the server still sends chunked data
-> the browser will not understand it.

Solutions c), as I told before would also be good, but then you would not
see PHP's headers in get_response_headers(). And code is not much shorter or
simplier.

-----
Uwe Schindler
thetaphi@php.net - http://www.php.net
NSAPI SAPI developer
Bremen, Germany

> -----Original Message-----
> From: Uwe Schindler [mailto:thetaphi@php.net]
> Sent: Saturday, November 29, 2008 1:09 PM
> To: 'Arnaud Le Blanc'
> Cc: 'Lukas Kahwe Smith'; internals@lists.php.net; 'Christian Schmidt';
> 'Alex Leigh'; 'George Wang'
> Subject: RE: [PHP-DEV] [PATCH] Allow unsetting headers previously set
> usingheader()
> 
> Hallo Arnaud,
> 
> > […]
> 
> That is correct, you are able to remove the headers. For removing the Date
> header it would be enough to just use the `header_remove()` without setting
> it
> before. But if somebody does this, he knows what he is doing. I want to
> prevent PHP from doing things that the user cannot control or understand.
> Sun Webserver for example is able to compress output automatically and
> other
> things. So I am not sure what happens, if you remove headers. As source
> code
> of this webserver is not yet available (Sun wants to release it soon), I
> do
> not know *when* nsapi puts entries in his internal header hash table.
> Maybe
> its in protocol_start_response(), if so everything would be ok, and I can
> manually cleanup the complete webserver's hash table (solution b). But
> nsapi_response_headers from PHP called shows, that e.g. Date and Server
> are
> set *before* the request starts.
> 
> In my opinion, a call to `header_remove()` from PHP should only remove
> headers
> set by PHP (e.g. revert to the state, before the PHP script started).
> 
> > […]
> 
> That's not correct for at least apache:
> 
> php_apache_sapi_flush(void *server_context)
> {
> 	...
> 	sapi_send_headers(TSRMLS_C);
> 
> 	r->status = SG(sapi_headers).http_response_code;
> 	SG(headers_sent) = 1;
> 
> 	if (ap_rflush(r) < 0 || r->connection->aborted) {
> 		php_handle_aborted_connection();
> 	}
> }
> 
> The flush function just calls sapi_send_headers so it explicitely send the
> headers, so my approach of do not implementing a header_handler would also
> correctly send the headers in this case. For NSAPI it is not even possible
> to send headers explicitely, you can only set them in an internal
> hashtable
> of the web server.
> 
> For a typical NSAPI module the workflow is the following:
> Any "Service" handler from webserver's config sets headers in the internal
> hash table. Before it starts to write output, it may also set the response
> code. All this is not sent to the client immediately. The headers and
> everything is sent on protocol_start_response(). After that you cannot set
> any headers anymore and you have to write to the output stream.
> 
> The PHP module calls the webserver's protocol_start_response() in
> sapi_send_headers(), so even when you flush, this must be called (in the
> current version of output buffering there is still a bug about this, which
> is fixed in the new version, I think). I repaired this by also
> implementing
> flush for nsapi in my new version, which calls like apache
> sapi_send_headers(TSRMLS_C). This fixes a very old bug for NSAPI.
> 
> So in my opinion for the server it is no difference when to set the
> headers
> (immediately after the call to `header()`) or shortly before starting the
> reponse. And this is how CGI works - and this would be enough for NSAPI,
> too.
> 
> The only thing that would not work is explicitely removing headers like
> "Date:", but for this use case header_handler() could only be implemented
> for the op SAPI_HEADER_DELETE. But with CGI this would not be possible.
> 
> Uwe
