PHP's $_SERVER['HTTP_IF_NONE_MATCH'] is always empty however Firefox's Web Developer Network Requests tab shows the request header as being If-Modified-Since: "Tue, 27 Jun 2017 09:08:23 GMT". Cache is not disabled and my .htaccess file contains the following:
RewriteEngine on
RewriteRule .* - [E=HTTP_IF_NONE_MATCH:%{HTTP:If-None-Match}]
How do I get PHP's $_SERVER['HTTP_IF_NONE_MATCH'] to return the request header properly or how do I access that request header in an alternative manner? I came across getenv('HTTP_IF_MODIFIED_SINCE') though that didn't return anything either. Is it possible there is something in the php.ini file that needs to be changed? It is a fairly fresh server setup.
You should add a If-None-Match http header to your server. If you do so the HTTP_IF_NONE_MATCH server variable should contain the value of the set If-None-Match header.
curl http://my/endpoint --header 'If-None-Match: "my-custom-etag-value"'
Related
I'm building a REST API.. Sometimes the server returns the response with chunked transfer encoding? Why is that?!
Why can't the server always return the response in the same encoding?
The problem is that I don't know how to read the data when its returned as chunked!?
update
neeed moore downvotes... to breeeath...
Assuming your server is using Apache, this is expected behaviour. You can disable it by putting this line in your .htaccess file:
SetEnv downgrade-1.0
However, you should consider modifying your reading code to just support different content encodings. What library are you using to make the HTTP request? Any reasonable HTTP library can handle chunked requests. If your requesting code is written in PHP, use curl. http://php.net/manual/en/book.curl.php
Taken from Server Fault:
specify the "Content-Length' header, so server knows, what's the size of the response
use HTTP 1.0 at the requester's side
A problem may be that Apache is gzipping your download, taking care of correcting the Content-Length, or in your case, adding the header
Content-Encoding: chunked
You can add a .htaccess RewriteRule to disable gzip:
RewriteRule . - [E=no-gzip:1]
I'm trying to create a simple Web service that has to return various HTTP codes depending on some conditions, mainly the existence of files related to the specific resources requested via URI. However, i'm stuck on a really strange behaviour i keep getting when I try to generate a 404 header via PHP.
The first snippet, that works, is as follows:
$isNotFound = TRUE;
if ($isNotFound) header('HTTP/1.1 404 Not Found');
Using a simple command-line cURL to request the URI behind which this script runs, I get:
$ curl -LI http://www.example.com/
HTTP/1.1 404 Not Found
Date: Wed, 18 Sep 2013 20:57.25 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.8
Vary: Accept-Encoding
Connection: clse
Content-Type: text/html
Now, the second take is like this:
$isNotFound = FALSE;
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$isNotFound = TRUE;
}
if ($isNotFound === TRUE) {
header('HTTP/1.1 404 Not Found');
}
Running cURL again, this time I get this:
$ curl -LI http://www.example.com/
HTTP/1.1 200 OK
Date: ...
The header is the same as the former, except for the code. To check the obvious, I also printed the value of $isNotFound just before the last if, and it was indeed evaluated to TRUE, so the header call with the 404 code should be executed. I also added an exit() inside the last if, and another header() at the end of the script, giving other codes in response (like 302), and the result is always that the header inside the if is ignored.
I managed to make the second script work by explicitly specifying the request method as GET in the cURL call:
$ curl -X GET -LI http://www.example.com/
HTTP/1.1 404 Not Found
Date: ...
I also had the doubt that cURL wasn't using GET as the default method, but printing the $_SERVER array showed that the request method was indeed GET.
So, what is the reason of this strange behaviour? Is cURL's fault when using the implicit GET method, or is something happening inside PHP? Or maybe i'm so tired that i'm missing something trivial?
Thank you guys, and sorry for the long post.
Next time read the manual:
-I, --head
(HTTP/FTP/FILE) Fetch the HTTP-header only! HTTP-servers feature the
command HEAD which this uses to get nothing but the header of a
document. When used on an FTP or FILE file, curl displays the
file size and last modification time only.
(or your webserver log files, or your TCP stream)
Background
Part of my application's responsibility is handling requests for static resources (CSS, JavaScript, images) in a controlled manner. Based on some application logic, it will return one from a selection of different files that might be served on that URL at different times and to different users. These are therefore static files, but delivered in a dynamic way.
The application is based on Symfony Components and the serving of these static-ish files is handled by the BinaryFileResponse class.
The bootstrap code calls the trustXSendfileTypeHeader method:
\Symfony\Component\HttpFoundation\BinaryFileResponse::trustXSendfileTypeHeader();
The application uses some internal logic based on configuration and the detection and use of apache_get_modules() to determine availability. If XSendfile is available and the configuration says to use it, it sets the X-Sendfile-Type header:
if ($useHeader === true) {
$request->headers->set('X-Sendfile-Type', $header);
}
$response = new BinaryFileResponse($filename);
Problem
When I run this with the configuration set to never use XSendfile, or through the PHP built-in web server, which obviously does not support XSendfile, everything is perfect.
When I utilise XSendfile, it also works -- most of the time.
Every so often, typically if I press the f5 key 3-4 times in quick succession, "something" wigs out and I get a garbled response. For example, this is supposed to be a JavaScript file (copied from "Response" tab under "Net" in Firebug):
hxYîãx��HTTP/1.1 200 OK Date: Tue, 05 Feb 2013 14:49:10 GMT Server:
Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.4.6-1ubuntu1.1
Cache-Control: public Last-Modified: Tue, 29 Jan 2013 13:33:23 GMT
Accept-Ranges: bytes Content-Transfer-Encoding: binary ETag:
"10426f-9f6-0" Vary: Accept-Encoding Content-Encoding: gzip
Content-Length: 1011 Keep-Alive: timeout=5, max=98 Connection:
Keep-Alive Content-Type: application/javascript
������VmoÛ6þ,ÿkÀ²ãIý°~q [Üt]
XÑt¶H¤#Rv¼Àÿ}w(YSÀØ2yïå¹*¾Á>¯¥¥,è) Æ^Ât¸BaÆ\éjgäjí
Î&ð*¸Åí¸tY!³Ç$Óe"jÞ![#,n®®oï®A¨þ¸þù××Þ©¼¼ôÇêÚd¹49mv°ÔrtBÖ^;WÍÓÔg´Y¥´FéôÁR9o°35Îà^º´N=UÐèEµ¢XE¸íÒ%ª°¨Úò7¬KñT¾{;£ÈrTnß³étUè{QÀçÍn·:'üJëQÍÄËZeNjOàyÕÁ:#3wö~4Òét1ù$µeN)RD|
¶FTØJ·ß½¥¨¸õGç >9TyÜxzgl-J:) b«9ûAQ½KXÉ!yÐÓ]
óÆÎ#W¡?¢vún·7j©ÿ¢ðõÖGEÁy\ºp¤÷cKxf?ï*¼Éç0^ïîÌÇ°ñDQ¸mYJ|4t¾ñæËÛ¯Å
¨6:çøp(}þÑò|LÂ;Õ(#v¹* /[¨U|xª
æ]ÍyìjµòÛ¯p?4sI¥"v÷ôp|uQ4ò4&Ï·$eÒc¸ xo%7Ôi´2ñx;TuÙj23 áÊ%ħ¿¹lÌwÀS.&ÏØß7¸}ó
ZXzå k2'Zdùè
�¦ºû-Ù[Ó²ÿU(¯¤¥=pÃjô¾ç]]Øhhô²×ÙãÚÍ4¨[!Õ}'Òþ^Ð�ûxÿ#+ÚVÞ~áÌáy?d
aíD¹·U×ÃÚ] õ5íÃø¨o÷ÂAvUÆmÍaày`¦ä©A?mL[-}®(ÿË
d°öò¬}Ç¢³Çp1À^6%0 hTô^ts´ÞíWô
fO¶ö¢ÎNÜæ·HîUôÔ¶±ÌCµsxh.9åçi Û·_ÈÞØ_ÄãY_Ö}G<ì°ý2wÔ¿aw8/þù\ã±þ"0C
oÂh'tE¶À¤¥7I½éßRt.s?á^d|k/Æ)wRw÷cG¿<Þ
¼´°/^ø*ʤAVZ×y¿zÅΪ¥[²Õ1ò_Vµæï_YXÁÕö ��YXÁÕö ��
Note the presence of the headers in the response body, and the rest of it which is clearly not JavaScript. There are also some spurious characters at the start, which possibly is what leads to the headers being pushed to the body. I have tried to determine if this content is the result of gzipping, but I can't confirm that yet. (See also update below)
Question
Firstly, is BinaryFileResponse even the correct class to use for serving text (non-binary) files? The documentation for the class only says "BinaryFileResponse represents an HTTP response delivering a file." This isn't very detailed but it doesn't say anything about it being exclusively for "binary" files. However the name has its own implications, why didn't Fabien just call this class FileResponse?
Secondly, and more importantly, what could be causing this? I don't believe it is a browser issue because it is repeatable in both Firefox and Chrome. Is this a bug in the XSendfile module or in the BinaryFileResponse class perhaps? (I am likely to think it is not the former because I have used it before in a more "raw" way not via Symfony Components, with no such issues).
Has anyone else experienced this? Any idea where I should even start looking to track this down? I've looked at the BinaryResponseFile source code but it doesn't really do much with XSendfile, just sets the relevant header and prevents content in the response body, from what I can see.
Update
I've just noticed a couple of things about these garbled responses:
There are no actual headers being sent at all, i.e. on the "Headers" tab in Firebug, for the garbled responses, it only lists Request headers and doesn't even show the heading for Response headers.
Even if I set some custom header on the Response in PHP, that header does not appear at all in the garbled responses (as a header or in the response body), but the custom headers appear correctly for the responses that aren't broken.
First, let me say that I don't have any experience with this Apache module, but I'll try to guide you through a general error deduction:
You should check if you can reproduce it more reliably. While a web browser might be ok to try it out, you should go for something like curl and do the request multiple times, for example using a bash for-loop.
for i in `seq 1 5`; do curl -v http://localhost/xsendfile-url; done
The fact that the Connection: Keep-Alive header is set and that there are some weird characters before the actual HTTP header lead me to believe that you won't be able to reproduce this problem with separated curl calls, because it will open a fresh connection each time. So try this to check if that gives you the weird behavior (curl has keep alive on by default):
curl -v http://localhost/xsendfile-url http://localhost/xsendfile-url http://localhost/xsendfile-url
Using this, you could go to the projects github issue page and report your findings. Most probably they will there help you in telling you why mod_xsendfile is behaving the way it is or that you have found a bug.
I'm building a REST API.. Sometimes the server returns the response with chunked transfer encoding? Why is that?!
Why can't the server always return the response in the same encoding?
The problem is that I don't know how to read the data when its returned as chunked!?
update
neeed moore downvotes... to breeeath...
Assuming your server is using Apache, this is expected behaviour. You can disable it by putting this line in your .htaccess file:
SetEnv downgrade-1.0
However, you should consider modifying your reading code to just support different content encodings. What library are you using to make the HTTP request? Any reasonable HTTP library can handle chunked requests. If your requesting code is written in PHP, use curl. http://php.net/manual/en/book.curl.php
Taken from Server Fault:
specify the "Content-Length' header, so server knows, what's the size of the response
use HTTP 1.0 at the requester's side
A problem may be that Apache is gzipping your download, taking care of correcting the Content-Length, or in your case, adding the header
Content-Encoding: chunked
You can add a .htaccess RewriteRule to disable gzip:
RewriteRule . - [E=no-gzip:1]
I know in PHP, it sends the X-Powered-By header to have the PHP version.
I also know by appending some checksums, you can get access to PHP's credits, and some random images (more info here).
I also know in php.ini you can turn expose_php = off.
But here is something I have done on a few sites, and that is use
header('X-Powered-By: Alex');
When I view the headers, I can see that it is now 'Alex' instead of the PHP version. My question is, will this send the previous PHP header first (before it reaches my header(), and is it detectable by any sniffer program? Or are headers 'collected' by PHP, before being sent back to the browser?
By the way, this is not for security by obscurity, just curious how headers work in PHP.
You can set expose_php = Off in your php.ini if you don't want it to send X-Powered-By header.
PHP first compiles everything (including which headers have which values ) and then start the output, not vice-versa.
PHP is also detectable with its own easter eggs, you can read about this topic here : PHP Easter Eggs
See Apache Tips & Tricks: Hide PHP version (X-Powered-By)
Ups… As we can see PHP adds its own
banner:
X-Powered-By: PHP/5.1.2-1+b1…
Let’s see how we can disable it. In
order to prevent PHP from exposing the
fact that it is installed on the
server, by adding its signature to the
web server header we need to locate in
php.ini the variable expose_php and turn it off.
By default expose_php is set to On.
In your php.ini (based on your Linux
distribution this can be found in
various places, like /etc/php.ini,
/etc/php5/apache2/php.ini, etc.)
locate the line containing expose_php
On and set it to Off:
expose_php = Off
After making this change PHP will no
longer add it’s signature to the web
server header. Doing this, will
not make your server more secure… it will just prevent remote hosts to
easily see that you have PHP installed
on the system and what version you are
running.
In PHP, headers aren't sent until PHP encounters its first output statement.
This includes anything before the first <?php.
This is also why setcookie sends throws a warning if you try to use it after something has been output:
Warning: Cannot modify header
information - headers already sent by
(output started at
/path/to/php/file.php:100) in
/path/to/php/file.php on line 150
Note that none of this applies if output buffering is in use, as the output will not be sent until the appropriate output buffering command is run.
To get rid of the X-Powered-By header without having access to php.ini, simply add an empty header.
<?php header('X-Powered-By:'); ?>
This overwrites the default X-Powered-By header with an empty value an though most clients and applications act like this header was not sent at all.
As noticed before, this must be inserted into the code before any output is sent.
And to answer your question:
Only your X-Powered-By header will be sent because it gets replaced by your header with the same name. So it can't be detected by a 'sniffer'.
Headers are "collected" by PHP before being sent back to the browser, so that you can override things like the status header. The way to test it is go to a command prompt, and type:
telnet www.yoursite.com 80
GET /index.php HTTP/1.1
[ENTER]
[ENTER]
And you'll see the headers that are sent in the response (replace /index.php with the URL of your PHP page after the domain.)
To hide X-Powered-By: PHP/7.x.x , if you are using Share Hosting then add the following code in .htaccess file
Header always unset X-Powered-By
Header unset X-Powered-By
Then reload the browser or clear the cache using the LiteSpeed Cache plugin: https://en.wordpress.org/plugins/litespeed-cache/
My question is, will this send the previous PHP header first (before it reaches my header(), and is it detectable by any sniffer program? Or are headers 'collected' by PHP, before being sent back to the browser?
No, it does not send the previous PHP header first. Headers are either sent or not sent (in complete, as one batch) in PHP. By default your headerDocs call replaces a previous header with the same name (unless you specify something different with the second parameter).
Note: If PHP would not collect the headers, it would not be able to replace one.
As it does not sent it earlier, it is not detectable with a sniffer program.
So yes, headers are collected by PHP and are send the moment "the real" output starts (HTTP response body).
See as well headers_sentDocs.
PHP has a built-in function to remove headers: header_remove().
To remove the X-Powered-By header, you can use:
<?php
header_remove(
name: 'X-Powered-By'
);
As you can see, you only have to pass the header name as a string as parameter, and you are done.
Note that name parameter is parsed not case-sensitive, so you are fine calling it with x-powered-by as well.
Since PHP 8.0.0 when calling the function without the name parameter, all previously set headers will be unset.