In the absence of contrary advice, it appears there was nothing much wrong my implementation of the p-r-g model. Firefox, IE, and Opera work exactly as intended and Safari just blindly puts every GET into the history. But Safari's history is easily handled by inserting hidden or readonly freshness tokens into the form so that you can recognize a stale form.
The stumbling block is a Chrome quirk and I don't see a pure PHP/HTML way around it. (Duly reported, BTW.)
Here is new code, only a smidgen more complex, that makes the behavior very clear. Just do a few submits, typing something different in field Inval every time, then look back through the history.
Chrome always correctly fetches a new copy of the view, even if you go back into the history. But if you revisit visit #x, Chrome will override the pre-population values for any fields you touched on visit #x, putting in stale data instead of what the fresh view specified. (Check the mismatch between the data in field Inval and the prepopulation value shown to its right.) This is fixable by using JavaScript to re-pre-populate the corrupted fields by copying the prepopulation data into them from hidden elements you embed in the page.
The new VIEW.PHP
<?php # view.php
session_start();
header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: -1');
# Format timestamp and counter
$tval = date("H:i:s");
$cval = !isset($_SESSION['counter']) ? 0 : $_SESSION['counter'];
# Format last-posted values for display but (mostly) not for prepopulation
$lphiddn = !isset($_SESSION['xhiddn']) ? '' : htmlentities($_SESSION['xhiddn'],ENT_QUOTES,'UTF-8',FALSE);
$lpcount = !isset($_SESSION['xcount']) ? '' : htmlentities($_SESSION['xcount'],ENT_QUOTES,'UTF-8',FALSE);
$lpinval = !isset($_SESSION['xinval']) ? '' : htmlentities($_SESSION['xinval'],ENT_QUOTES,'UTF-8',FALSE);
# Format pre-population data
$phiddn = "H $tval"; // H 00:00:00
$pcount = "(#$cval) $tval"; // (#1) 00:00:00
$pinval = $lpinval; // whatever was last posted for this field
echo <<<HTM
<pre style='font-family: monospace;'>
<form method='POST' action='post.php'>
Hiddn <input type='hidden' name='hiddn' value='$phiddn'/> prepop=$phiddn lastpost=$lphiddn
Count <input type='text' name='count' value='$pcount'/> prepop=$pcount lastpost=$lpcount
Inval <input type='text' name='inval' value='$pinval'/> prepop=$pinval = lastpost=$lpinval
Submt <input type='submit' name='doit' value='Submit'/>
</form>
</pre>
HTM;
?>
The new POST.PHP
<?php # post.php
session_start();
header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: -1');
if(!isset($_SESSION['counter'])):
$_SESSION['counter'] = 1;
else:
$_SESSION['counter'] += 1;
endif;
$_SESSION['xhiddn'] = !isset($_POST['hiddn']) ? '' : $_POST['hiddn'];
$_SESSION['xcount'] = !isset($_POST['count']) ? '' : $_POST['count'];
$_SESSION['xinval'] = !isset($_POST['inval']) ? '' : $_POST['inval'];
header("HTTP/1.1 303 See Other");
header('Location: view.php');
?>