В RFC HTTP про каждый метод написано две короткие характеристики: safe и idempotent. В переводе — «безопасный» и «идемпотентный». Слова шероховатые, но за ними простые мысли — и любимая часть собеседований по REST.
Safe: метод не меняет данные на сервере
Метод считается safe (безопасным), если его вызов не должен менять состояние сервера. Сделали запрос — пришёл ответ. Что на сервере было до запроса, то и осталось после.
Safe-методы: GET, HEAD, OPTIONS.
Несейф (меняют состояние): POST, PUT, PATCH, DELETE.
Это не просто маркировка для красоты. Из неё вытекают конкретные последствия для разработки:
- Браузер и прокси могут кэшировать ответы safe-методов — и кэшируют. Поэтому повторный GET часто прилетает не из сети, а из кэша.
- Поисковые роботы и превью-сервисы свободно дёргают GET-URL. Если на GET повесить удаление ресурса (так делать нельзя!) — робот это удалит, просто пройдя по ссылке.
- Браузер может перезапрашивать GET при кнопке «обновить» без подтверждения. С POST — покажет диалог «повторить отправку формы?».
Поэтому правило: если метод что-то меняет на сервере — никогда не GET. Удаление через GET /deletePost?id=42 — классическая ошибка REST-новичков, которая ловит на пустом месте.
Idempotent: тот же результат, сколько ни дёргай
Метод идемпотентный, если повторный вызов того же запроса приводит к тому же состоянию сервера, что и одиночный. Один раз вызвал — ресурс в нужном виде. Вызвал ещё раз с тем же запросом — ресурс всё в том же виде. И так далее.
Аналогия: переключатель света. Положение «вверх» (включено) — одно состояние. Сколько ни щёлкай «вверх», свет уже включён, состояние то же. А вот двойной щелчок (вверх-вниз) — уже другая операция, оставляющая другое состояние.
Идемпотентные методы: GET, HEAD, OPTIONS, PUT, DELETE.
Не идемпотентный: 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, но состояние в обоих случаях одно: ресурса нет. Идемпотентно.
Зачем это нужно знать на практике
Идемпотентность — не академическая формальность. Из неё вытекают две очень практические вещи.
Безопасные повторы при ошибках
Сеть нестабильна. Запрос может уйти, но ответ — потеряться. Клиент не знает, дошёл запрос до сервера или нет. Что делать?
- Если метод идемпотентен (GET, PUT, DELETE) — можно спокойно повторить запрос. Даже если первый дошёл, повторный не сломает данные.
- Если метод не идемпотентен (POST) — повтор опасен. Может оказаться, что первый запрос создал заказ, а повтор создаст второй такой же. Клиент получит два заказа, пользователь рассержен.
Реальные сервисы решают это разными способами: добавляют заголовок Idempotency-Key к POST-запросам (Stripe, например), требуют от клиента уникального токена, генерят дедуп-id на сервере. Но базовый принцип — идемпотентные ретраить можно, неидемпотентные — осторожно.
Кэширование и оптимизация
Браузер и CDN кэшируют ответы safe-методов смело. Идемпотентные несейф-методы (PUT, DELETE) не кэшируются по содержимому, но повторы безопасны. На POST кэш не работает в принципе.
Свод в одну таблицу
| Метод | Safe | Idempotent | Кэшируется |
| GET | да | да | да |
| HEAD | да | да | да |
| OPTIONS | да | да | нет |
| PUT | нет | да | нет |
| DELETE | нет | да | нет |
| POST | нет | нет | нет |
| PATCH | нет | зависит | нет |
Эту табличку стоит запомнить. На собеседованиях её любят спрашивать почти дословно.