Генерируете случайный токен, помещаете его в сессию / кеш / базу и отправляете на страницу в какой-нибудь метатег в head, например.
При отправке POST запроса фронт берёт этот токен и отправляет на бэк.
Бэк проверяет его наличие и срок его действия. Если токен был валидный, то в ответ генерируете новый и снова отправляете на страницу, если нет - заставляете пользователя обновить страницу. В обоих случаях токен инвалидируется и удаляется.
Один раз в 10 / 100 / 1000 проверок токена запускаете чистильщик невалидных токенов с истёкшим сроком действия.