A nonce is valid for a period rather than until it is used, and that period is two ticks of half a day. It produces a bug report nobody can reproduce on demand: a long form, or a tab left open over lunch, is submitted and rejected — sometimes, depending on when the page was rendered.
// 1 = current tick, 2 = the previous one, false = neither
$check = wp_verify_nonce( $_POST['_wpnonce'], 'save_shipment_' . $id );
if ( false === $check ) {
wp_die( 'This page has expired. Reload it and try again.' );
}
// changes the tick for everything, admin included
add_filter( 'nonce_life', function () {
return 4 * HOUR_IN_SECONDS;
} );
Because wp_verify_nonce() accepts the previous tick as well as the current one, the effective lifetime is anywhere between twelve and twenty-four hours depending on where in the tick the form was rendered — which is exactly why it fails intermittently and never on the developer’s machine. The second cause is worse: a nonce printed into a page that a full-page cache then serves to a different visitor verifies as false for everyone, because the token is derived from the user id and the session. Filtering nonce_life changes it globally, so for one genuinely long-lived form the better answer is to refresh the token from the client. And a returned 2 is still valid — treating anything other than 1 as failure quietly reintroduces the same report.