Skip to content

Instantly share code, notes, and snippets.

@theseer
Created March 24, 2019 16:36
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save theseer/dff2d65ac80df66afc0a4cca68f4030c to your computer and use it in GitHub Desktop.
Save theseer/dff2d65ac80df66afc0a4cca68f4030c to your computer and use it in GitHub Desktop.
What happend to the `If-Modified-Since` header?

If-Modified-Since, PHP CLI Server and Firefox

Explanation

When sending the cache control header Cache-Control: no-cache, must-revalidate along with a Last-Modified timestamp, the browser is required to send along an If-Modified-Since header for the next request to the same URL.

The server then is entitled to return HTTP/1.1 304 Not modified in case the content didn't change and the stored copy may still be used.

Sample Code

<?php

$time = 'Wed, 21 Oct 2016 07:28:00 GMT';
header('Cache-Control: no-cache, must-revalidate');
header('Last-Modified: ' . $time );

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $time === $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
    http_response_code(304);
    header('X-MODIFIED-SINCE: MATCH'); 
    die();
}

http_response_code(200);
header('X-CONTENT-RETURN: YES');
echo '<a href="/index.php">link to index.php</a>';

Running it

Firefox 66, NGINX & PHP-FPM

First Request

Request Headers

Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-DE,de;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache

Response Headers

HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Sun, 24 Mar 2019 16:11:16 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/7.3.3
Cache-Control: no-cache, must-revalidate
Last-Modified: Wed, 21 Oct 2016 07:28:00 GMT
X-CONTENT-RETURN: YES

Second Request

Request Headers

Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-DE,de;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1/index.php
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
If-Modified-Since: Wed, 21 Oct 2016 07:28:00 GMT

Response Headers

HTTP/1.1 304 Not Modified
Server: nginx/1.14.2
Date: Sun, 24 Mar 2019 16:14:26 GMT
Connection: keep-alive
X-Powered-By: PHP/7.3.3
Cache-Control: no-cache, must-revalidate
Last-Modified: Wed, 21 Oct 2016 07:28:00 GMT
X-MODIFIED-SINCE: MATCH

Perfect. That's the way it's supposed to work. Now let's try the same thing without NGINX

Firefox 66, PHP-CLI Server

php -S 127.0.0.1:8080 index.php

First Request

Request Headers

Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-DE,de;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1

Response Headers

HTTP/1.1 200 OK
Host: 127.0.0.1:8080
Date: Sun, 24 Mar 2019 16:18:54 +0000
Connection: close
X-Powered-By: PHP/7.3.3
Cache-Control: no-cache, must-revalidate
ETag: some-etag-value
Last-Modified: Wed, 21 Oct 2016 07:28:00 GMT
X-CONTENT-RETURN: YES
Content-type: text/html; charset=UTF-8

Second Request

Request Headers

Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-DE,de;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1:8080/index.php
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1

WTF?

What happend to the If-Modified-Since header?

Question

Of course it works in Chromium & Epiphany. ;-)

So, is this a Bug in

  • My Script
  • The PHP CLI Server
  • Firefox

I wouldn't know how it could be a bug in my small sample script as it works when run via nginx+php-fpm and with either server for chrome/chromium and epiphany.

The only difference in the headers I can see is the Format used for the Date response header, the other headers are ( should be?) unrelated. Or not?

It might be an overly picky Firefox not liking the date format +0000 rather than GMT. It would be easy to simply set a custom Date header, of course. Problem is: PHP's Cli Server adds its own no matter what. Yes, it will have two Date header lines in that case. That's not helping ;)

@kelunik
Copy link

kelunik commented Mar 24, 2019

Yes, I'm pretty sure Date headers must be in GMT and +0000 is invalid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment