В RFC HTTP про каждый метод написано две короткие характеристики: safe и idempotent. В переводе — «безопасный» и «идемпотентный». Слова шероховатые, но за ними простые мысли — и любимая часть собеседований по REST.

Safe: метод не меняет данные на сервере

Метод считается safe (безопасным), если его вызов не должен менять состояние сервера. Сделали запрос — пришёл ответ. Что на сервере было до запроса, то и осталось после.

Safe-методы: GETHEADOPTIONS.

Несейф (меняют состояние): POSTPUTPATCHDELETE.

Это не просто маркировка для красоты. Из неё вытекают конкретные последствия для разработки:

  • Браузер и прокси могут кэшировать ответы safe-методов — и кэшируют. Поэтому повторный GET часто прилетает не из сети, а из кэша.
  • Поисковые роботы и превью-сервисы свободно дёргают GET-URL. Если на GET повесить удаление ресурса (так делать нельзя!) — робот это удалит, просто пройдя по ссылке.
  • Браузер может перезапрашивать GET при кнопке «обновить» без подтверждения. С POST — покажет диалог «повторить отправку формы?».

Поэтому правило: если метод что-то меняет на сервере — никогда не GET. Удаление через GET /deletePost?id=42 — классическая ошибка REST-новичков, которая ловит на пустом месте.

Idempotent: тот же результат, сколько ни дёргай

Метод идемпотентный, если повторный вызов того же запроса приводит к тому же состоянию сервера, что и одиночный. Один раз вызвал — ресурс в нужном виде. Вызвал ещё раз с тем же запросом — ресурс всё в том же виде. И так далее.

Аналогия: переключатель света. Положение «вверх» (включено) — одно состояние. Сколько ни щёлкай «вверх», свет уже включён, состояние то же. А вот двойной щелчок (вверх-вниз) — уже другая операция, оставляющая другое состояние.

Идемпотентные методы: GETHEADOPTIONSPUTDELETE.

Не идемпотентный: POST.

Идемпотентность PATCH зависит от реализации — если PATCH передаёт новые значения полей, он идемпотентен; если передаёт относительное изменение («увеличить счётчик на 1»), — нет. Стандарт ничего не обязывает.

Почему PUT идемпотентен, а POST — нет

Сравним два сценария.

POST: создаём новый пост. Если отправить запрос дважды — будет создано два разных поста с разными id. Состояние сервера после двух вызовов отличается от состояния после одного. Не идемпотентно.

POST /api/posts {"title": "Привет"}   → создан пост id=42
POST /api/posts {"title": "Привет"}   → создан пост id=43
POST /api/posts {"title": "Привет"}   → создан пост id=44

PUT: перезаписываем пост 42. Если отправить тот же запрос трижды — пост 42 трижды примет одни и те же значения. Состояние сервера после трёх вызовов то же самое, что после одного.

PUT /api/posts/42 {"title": "Новый"}  → пост 42 = {title: "Новый"}
PUT /api/posts/42 {"title": "Новый"}  → пост 42 = {title: "Новый"}
PUT /api/posts/42 {"title": "Новый"}  → пост 42 = {title: "Новый"}

То же и с DELETE: первый запрос удалил — ресурс больше не существует. Повторный запрос придёт на несуществующий ресурс — сервер вернёт 404 или 204, но состояние в обоих случаях одно: ресурса нет. Идемпотентно.

Зачем это нужно знать на практике

Идемпотентность — не академическая формальность. Из неё вытекают две очень практические вещи.

Безопасные повторы при ошибках

Сеть нестабильна. Запрос может уйти, но ответ — потеряться. Клиент не знает, дошёл запрос до сервера или нет. Что делать?

  • Если метод идемпотентен (GETPUTDELETE) — можно спокойно повторить запрос. Даже если первый дошёл, повторный не сломает данные.
  • Если метод не идемпотентен (POST) — повтор опасен. Может оказаться, что первый запрос создал заказ, а повтор создаст второй такой же. Клиент получит два заказа, пользователь рассержен.

Реальные сервисы решают это разными способами: добавляют заголовок Idempotency-Key к POST-запросам (Stripe, например), требуют от клиента уникального токена, генерят дедуп-id на сервере. Но базовый принцип — идемпотентные ретраить можно, неидемпотентные — осторожно.

Кэширование и оптимизация

Браузер и CDN кэшируют ответы safe-методов смело. Идемпотентные несейф-методы (PUTDELETE) не кэшируются по содержимому, но повторы безопасны. На POST кэш не работает в принципе.

Свод в одну таблицу

Метод Safe Idempotent Кэшируется
GET да да да
HEAD да да да
OPTIONS да да нет
PUT нет да нет
DELETE нет да нет
POST нет нет нет
PATCH нет зависит нет

Эту табличку стоит запомнить. На собеседованиях её любят спрашивать почти дословно.