ERR_CACHE_MISS / Confirm Form Resubmission (tested)

net::ERR_CACHE_MISS with the heading Confirm Form Resubmission appears when you go back or forward to a page that was the answer to a POST, and the browser has no stored copy. We reproduced it in Chromium 153, counted how often the server got the POST again, and tested the fixes.

1. What the error means

A page you get from submitting a form is the result of a POST. To show it again, the browser can either use a stored copy or send the POST again. Sending it again could repeat an order or a payment, so Chrome will not do that on back or forward. If it has no stored copy, it shows an error page instead.

This is the exact text Chromium 153 showed on back navigation in our test:

Confirm Form Resubmission

This web page requires data that you entered earlier in order to be
properly displayed. You can send this data again, but by doing so you
will repeat any action this page previously performed.

Press the reload button to resubmit the data needed to load the page.
ERR_CACHE_MISS

It is not a server error. Your server never sees a request when this page appears.

2. Our test setup

A Node server served a form at /form that POSTs to /submit. The server counted every POST. Playwright then did: submit, reload, go to another page, back, reload, forward, back. We changed only the POST response between runs.

In Playwright, the failed back navigation throws page.goBack: net::ERR_CACHE_MISS and the page URL becomes chrome-error://chromewebdata/. Note: the default Playwright headless shell renders an empty error page (39 bytes, no text). The full Chromium build in headless mode (channel: 'chromium') rendered the full text above.

3. Results: what back and reload do after a POST

POST responseBack / forward to resultReload of resultTotal POSTs for 1 submit
200, no Cache-ControlShown from cache, 0 new POSTsRe-POSTs (same body), no prompt in headless3
200, Cache-Control: no-cacheShown from cache, 0 new POSTsRe-POSTs3
200, Cache-Control: no-storenet::ERR_CACHE_MISS, Confirm Form ResubmissionRe-POSTs3
303 to /done (PRG)GET page, 0 POSTsGET /done, 0 POSTs1
302 to /doneGET page, 0 POSTsGET /done, 0 POSTs1
307 to /doneFrom cache, 0 POSTsRe-POSTs to /done4
200 + history.replaceState to /doneGET page, 0 POSTsGET /done, 0 POSTs1

"Total POSTs" is over the whole run of submit, two reloads and three back/forward moves. Three findings stand out:

4. The fix: Post/Redirect/Get with 303

Answer a successful POST with a redirect to a normal GET page. The history entry then holds the GET URL, not the POST.

// Node / Express
app.post('/order', async (req, res) => {
  const id = await saveOrder(req.body);
  res.redirect(303, `/order/${id}`);   // See Other: browser follows with GET
});
<?php // PHP
save_order($_POST);
header('Location: /order/' . $id, true, 303);
exit;

With 303, our run sent exactly 1 POST. Two reloads and three back/forward moves all became GET /done. No ERR_CACHE_MISS. 302 behaved the same in Chromium. 307 does not work: it keeps the method, so Chromium sent POST /done and every reload re-POSTed. Per RFC 9110, 308 also keeps the method (not measured here). See 301 vs 302 vs 307 vs 308.

Use 303 for PRG. Per RFC 9110 it is the status meant for "go GET this other URL". If the redirect does not fire at all in PHP, see PHP header Location not working.

5. Does history.replaceState after the POST help?

Partly. We added this script to the 200 result page:

<script>history.replaceState(null, '', '/done');</script>

Chromium then treated the entry as a GET to /done. Reload and back sent GETs, and the run had 1 POST total. But two catches:

6. Other fixes and when to use them

As a user who just sees the error: press reload only if you are fine sending the form again. Otherwise go back to the form page and start over.

Frequently asked questions

What causes net::ERR_CACHE_MISS?

Going back or forward to a page that came from a POST when the browser has no stored copy. In our Chromium 153 test it happened when the POST response had Cache-Control: no-store. Without no-store the page came from cache.

Does pressing reload send the form again?

Yes. In our test every reload of a POST result page sent the POST again. Reloading the ERR_CACHE_MISS page itself sent a POST with an empty body (Content-Length: 0).

How do I stop Confirm Form Resubmission on my site?

Use Post/Redirect/Get. Answer the POST with 303 See Other to a GET URL. In our test that cut the server POST count from 3 to 1 across two reloads and three back/forward moves.

Can I use 307 or 308 for Post/Redirect/Get?

No. 307 and 308 keep the method. Chromium sent POST to the redirect target and re-POSTed on reload. Use 303. 302 also turned into GET in our test, but 303 is the one meant for this.

Does history.replaceState fix it?

It worked in our test if you replace the URL with one that answers GET. Replacing with the same POST URL made reload send GET /submit, which returned 405. A server-side 303 is more reliable.

Written with AI assistance. Measured on 2026-10-02 with Chromium 153.0.8010.12 driven by Playwright (both the default headless shell and the full 'chromium' channel in headless mode) against a Node.js 24.21.0 test server that counted every POST. The headed Chrome reload dialog was not measured. PlainHeaders is our extension; Chromium, Playwright and Node.js are free.