I have a web page x.php
(in a password protected area of my web site) which has a form and a button which uses the POST
method to send the form data and opens x.php#abc
. This works pretty well.
However, if the users decides to navigate back in Internet Explorer 7, all the fields in the original x.php
get cleared and everything must be typed in again. I cannot save the posted information in a session and I am trying to understand how I can get IE7 to behave the way I want.
I've searched the web and found answers which suggest that the HTTP header should contain explicit caching information. Currently, I've tried this :
session_name("FOO");
session_start();
header("Pragma: public");
header("Expires: Fri, 7 Nov 2008 23:00:00 GMT");
header("Cache-Control: public, max-age=3600, must-revalidate");
header("Last-Modified: Thu, 30 Oct 2008 17:00:00 GMT");
and variations thereof. Without success. Looking at the returned headers with a tool such as WireShark shows me that Apache is indeed honouring my headers.
So my question is: what am I doing wrong?
IE will retain form contents on a back button click automatically, as long as:
You seem to have the cacheing in hand, so I'm guessing the latter may apply. (As mkoeller says, Firefox avoids this problem if the page is in the last few back clicks by keeping the page itself alive for longer than it's on the screen. However this is optional, and Firefox will revert to the same behaviour as IE and other browsers once you've browsed a few pages ahead and it has expired the old one.)
If you're creating your own form fields from script onload, then the browser has no way of knowing that the new input control is ‘the same’ as the old instance, so it can't fill it in with the previously-submitted value. In this case if you want it to play well with the back button you have to start storing data on the client.
Then you have to use some sort of state-keying so that each set of data is tied to exactly one instance of the page, otherwise going through multiple instances of the same form or having two browsers tabs open on the form at once will severely confuse your script.
And then you are starting to collect a lot of data if they're big forms, and if the client-side storage mechanism you're using is cookies you can start to lose data, as well as sending a load of unnecessary state nonsense with every HTTP request. Other client-side storage mechanisms are available but they're browser-specific.
In short: doing dynamically-generated forms nicely is a huge pain and it's probably best avoided if you can. Having a hidden form on the page that a script makes visible, thus allowing browsers to do their field-remembering-magic instead of giving you the task, is typically much easier.
While trying to further narrow down the problem, I've found the cause of my problem. I was using URLs which were being rewritten by Apache (i.e. I always accessed my page as http://foo.com/page
which is mapped by Apache to http://foo.com/page.htm
). Using the real URLs solved the problem and made IE7 happy, as long as I specify the proper HTTP header (Cache-Control
, Expires
, etc.).
Here is what I do in the PHP code to output headers which seem to make all browsers happy with the cache:
function emitConditionalGet($timestamp)
{
// See also http://www.mnot.net/cache_docs/
// and code sample http://simonwillison.net/2003/Apr/23/conditionalGet/
$gmdate_exp = gmdate('D, d M Y H:i:s', time() + 1) . ' GMT';
$last_modified = gmdate('D, d M Y H:i:s', $timestamp) . ' GMT';
$etag = '"'.md5($last_modified).'"';
// If the client provided any of the if-modified-since or if-none-match
// infos, take them into account:
$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false;
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH'])
? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : false;
if (!$if_modified_since && !$if_none_match)
{
return; // the client does not cache anything
}
if ($if_none_match && $if_none_match != $etag)
{
return; // ETag mismatch: the page changed!
}
if ($if_modified_since && $if_modified_since != $last_modified)
{
return; // if-modified-since mismatch: the page changed!
}
// Nothing changed since last time client visited this page.
header("HTTP/1.0 304 Not Modified");
header("Last-Modified: $last_modified");
header("ETag: $etag");
header("Cache-Control: private, max-age=1, must-revalidate");
header("Expires: $gmdate_exp");
header("Pragma: private, cache");
header("Content-Type: text/html; charset=utf-8");
exit;
}
function emitDefaultHeaders($timestamp)
{
$gmdate_exp = gmdate('D, d M Y H:i:s', time() + 1) . ' GMT';
$last_modified = gmdate('D, d M Y H:i:s', $timestamp) . ' GMT';
$etag = '"'.md5($last_modified).'"';
header("Last-Modified: $last_modified");
header("ETag: $etag");
header("Cache-Control: private, max-age=1, must-revalidate");
header("Expires: $gmdate_exp");
header("Pragma: private, cache");
header("Content-Type: text/html; charset=utf-8");
}
function getTimestamp()
{
// Find out when this page's contents last changed; in a static system,
// this would be the file time of the backing HTML/PHP page. Add your
// own logic here:
return filemtime($SCRIPT_FILENAME);
}
// ...
$timestamp = getTimestamp();
emitConditionalGet($timestamp);
emitDefaultHeaders($timestamp); //previously, this variable was mistyped as "$timestaml"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With