Введение
Каждый разработчик тратит часы на отладку API. Запрос не проходит, ответ приходит не тот, ошибка непонятная — знакомо? Разница между джуниором и сеньором часто в том, как быстро разработчик находит и исправляет проблему.
Эта статья — ваш систематический чек-лист отладки API. Не хаотичное «ну попробуй ещё раз», а пошаговый алгоритм: от первой ошибки до её решения. С инструментами, примерами кода и реальными кейсами.
Вы научитесь:
- ✅ Быстро классифицировать ошибку по HTTP-статусу
- ✅ Использовать правильные инструменты (curl, Postman, DevTools, прокси)
- ✅ Отлаживать CORS, аутентификацию, таймауты и SSL
- ✅ Читать и анализировать логи
- ✅ Строить воспроизводимые тест-кейсы
📋 Содержание
Алгоритм отладки: 7 шагов
Когда API-запрос не работает, следуйте этому алгоритму — от общего к частному:
Прочитайте HTTP-статус код
Статус код — первая подсказка. Он сразу сужает область поиска:
- 4xx — вы отправили неправильный запрос
- 5xx — сервер сломался
- 0 / Network Error — запрос не дошёл до сервера
Подробнее о каждом коде — в нашем полном справочнике HTTP-статусов.
Прочитайте тело ответа
Большинство API возвращают JSON с описанием ошибки:
{
"error": {
"code": "INVALID_PARAMETER",
"message": "Field 'email' must be a valid email address",
"field": "email",
"details": "Got value: 'not-an-email'"
}
}
Если ответ — HTML (страница ошибки веб-сервера), значит запрос не дошёл до приложения.
Воспроизведите через curl или Postman
Изолируйте проблему от вашего кода. Если через curl работает, а через код — нет, ошибка в вашем коде.
curl -v -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"name": "Иван", "email": "ivan@test.ru"}'
Сравните с документацией
Откройте Swagger/OpenAPI документацию API и сверьте:
- URL эндпоинта (включая версию:
/v1/vs/v2/) - HTTP метод (GET, POST, PUT, DELETE)
- Обязательные заголовки (
Content-Type,Authorization) - Формат тела запроса (JSON-поля, типы данных)
- Обязательные query-параметры
Проверьте аутентификацию
Половина ошибок 401/403 — из-за неправильного токена:
- Токен истёк?
- Передан в правильном заголовке? (
Authorization: Bearer ...vsX-API-Key: ...) - Нет лишних пробелов или переносов строк?
- Правильный формат? (
Bearerс большой буквы, пробел после)
Проверьте сетевое окружение
Иногда проблема не в коде и не в API:
- VPN / корпоративный прокси блокирует запрос?
- DNS резолвит правильный IP?
- Файрвол блокирует порт?
- SSL-сертификат валиден?
Минимизируйте запрос
Отправьте минимально возможный запрос с одним обязательным полем. Если он проходит — добавляйте поля по одному. Так вы найдёте, какое именно поле вызывает ошибку.
Ошибки 4xx: проблемы на стороне клиента
400 Bad Request
Что значит: сервер не может обработать запрос из-за синтаксической ошибки.
Что проверить:
- Валидный ли JSON в теле запроса? (кавычки, запятые, кодировка)
- Правильный
Content-Type? (application/jsonvsmultipart/form-data) - Типы данных совпадают? (число вместо строки, массив вместо объекта)
- Обязательные поля заполнены?
# Проверьте JSON через jq
echo '{"name": "Иван", "age": 30}' | jq .
# Частая ошибка — забыли Content-Type
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Иван"}'
401 Unauthorized
Что значит: отсутствует или невалидна аутентификация.
Что проверить:
- Токен передан? Есть ли заголовок
Authorization? - Формат правильный?
Bearer <token>(с пробелом, без кавычек) - Токен не истёк? Декодируйте JWT на jwt.io
- API-ключ vs Bearer-токен — правильный тип аутентификации?
# Проверьте JWT
echo "YOUR_JWT_TOKEN" | cut -d'.' -f2 | base64 -d 2>/dev/null | jq .
# Смотрите поле "exp" — Unix timestamp истечения
# Тестируйте с токеном
curl -v https://api.example.com/me \
-H "Authorization: Bearer eyJhbGci..."
403 Forbidden
Что значит: аутентификация пройдена, но нет прав на ресурс.
Что проверить:
- У пользователя есть нужная роль/разрешение?
- Ресурс принадлежит текущему пользователю?
- IP-адрес не в чёрном списке?
- Rate limit не превышен?
404 Not Found
Что значит: ресурс не существует.
Что проверить:
- URL написан правильно? (опечатки, регистр, слэш в конце)
- Версия API правильная? (
/v1/usersvs/v2/users) - ID ресурса существует?
- Базовый URL правильный? (production vs staging)
# Частые причины 404:
/api/users/ ← trailing slash может менять роут
/api/Users ← регистр имеет значение
/api/v1/users ← старая версия API
/api/user ← единственное число вместо множественного
422 Unprocessable Entity
Что значит: JSON валиден, но данные не проходят валидацию.
Что проверить:
- Формат email, телефона, даты
- Длина строки (min/max)
- Диапазон числа
- Уникальность значения (email уже существует?)
// Типичный ответ 422
{
"errors": {
"email": ["The email has already been taken."],
"age": ["The age must be at least 18."],
"name": ["The name field is required."]
}
}
429 Too Many Requests
Что значит: превышен лимит запросов (Rate Limiting).
Что проверить:
- Заголовки
X-RateLimit-Limit,X-RateLimit-Remaining,Retry-After - Бесконечный цикл запросов в коде?
- Нужно добавить кэширование?
// Обработка 429 с retry
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 1;
console.warn(`Rate limited. Retry after ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
Ошибки 5xx: проблемы на стороне сервера
500 Internal Server Error
Что значит: необработанное исключение на сервере.
Если это ваш API:
- Проверьте серверные логи:
tail -f /var/log/app/error.log - Воспроизведите запрос и ищите stack trace
- Проверьте подключение к базе данных
- Проверьте переменные окружения
Если это чужой API:
- Проверьте статус-страницу сервиса (status.example.com)
- Попробуйте позже
- Упростите запрос (может вы нашли баг в API)
- Напишите в поддержку с полным запросом и ответом
502 Bad Gateway
Что значит: прокси/балансировщик не может связаться с backend-сервером.
Что проверить:
- Backend-сервер запущен?
- Nginx/Apache конфигурация upstream корректна?
- Порт приложения правильный?
- Docker-контейнер работает?
docker ps
503 Service Unavailable
Что значит: сервис временно недоступен (перегрузка, обновление).
Что проверить:
- Нагрузка на сервер (CPU, RAM, диск)
- Не идёт ли деплой?
- Health-check проходит?
- Реализуйте retry с экспоненциальным backoff
504 Gateway Timeout
Что значит: backend не ответил вовремя.
Что проверить:
- Запрос не слишком тяжёлый? (огромная выборка из БД)
- Timeout в Nginx:
proxy_read_timeout - Медленный внешний сервис, который вызывает backend?
- Добавьте пагинацию для больших ответов
Сетевые ошибки
Когда запрос не доходит до сервера — статус кода нет, в браузере ERR_...:
ERR_CONNECTION_REFUSED
Сервер не слушает порт. Проверьте: запущен ли сервер, правильный ли порт, не блокирует ли файрвол.
ERR_NAME_NOT_RESOLVED
DNS не может резолвить домен. Проверьте: опечатка в домене, DNS-настройки, nslookup api.example.com.
ERR_CERT_AUTHORITY_INVALID
Проблема с SSL-сертификатом. Проверьте: не самоподписанный ли сертификат, не истёк ли, полна ли цепочка сертификатов.
# Проверить SSL-сертификат
openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null | openssl x509 -noout -dates
# notBefore=Feb 1 00:00:00 2026 GMT
# notAfter=Feb 1 00:00:00 2027 GMT
ETIMEDOUT / ERR_TIMED_OUT
Сервер не отвечает. Проверьте: доступен ли сервер (ping), не блокирует ли VPN/прокси, не превышен ли таймаут клиента.
# Диагностика: ping + traceroute
ping -c 3 api.example.com
traceroute api.example.com
# curl с таймаутом
curl --connect-timeout 5 --max-time 10 https://api.example.com/health
CORS: отдельная головная боль
CORS (Cross-Origin Resource Sharing) — механизм браузера, блокирующий кросс-доменные запросы. Если вы видите в консоли:
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present.
⚠️ CORS — это НЕ ошибка API. Это защита браузера.
curl и Postman не подвержены CORS. Если запрос работает через curl, но не из браузера — это CORS.
Как отлаживать CORS
-
Откройте DevTools → Network
Найдите запрос. Если есть предварительный
OPTIONS-запрос (preflight) — посмотрите его ответ:HTTP/1.1 200 OK Access-Control-Allow-Origin: http://localhost:3000 Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 86400 -
Проверьте, отправляет ли сервер заголовки
# Отправьте OPTIONS-запрос вручную curl -v -X OPTIONS https://api.example.com/users \ -H "Origin: http://localhost:3000" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: Content-Type, Authorization" # Ищите в ответе: Access-Control-Allow-* - Настройте сервер — добавьте CORS-заголовки. Подробнее — в нашей статье «5 способов исправить CORS-ошибку».
Инструменты отладки
🔧 Browser DevTools (F12)
Для чего: отладка фронтенд-запросов к API.
- Network tab — все запросы, заголовки, тело, timing
- Console tab — ошибки JavaScript, CORS-предупреждения
- Фильтр XHR/Fetch — только API-запросы
- Copy as cURL — ПКМ на запрос → скопировать как curl-команду
Лайфхак: кликните правой кнопкой на запросе в Network tab → «Copy as cURL» → вставьте в терминал. Это самый быстрый способ воспроизвести запрос.
🔧 Postman
Для чего: тестирование API без кода.
- GUI для запросов с сохранением в коллекции
- Переменные окружения (dev, staging, prod)
- Автотесты на ответы
- Генерация кода на любом языке
- Импорт OpenAPI/Swagger
🔧 Charles Proxy / mitmproxy
Для чего: перехват и анализ HTTPS-трафика.
- Видите все запросы приложения (включая мобильное)
- Модификация запросов и ответов на лету
- Throttling — эмуляция медленной сети
- Map Remote — подмена URL для тестирования
🔧 ngrok / webhook.site
Для чего: отладка Webhook-ов и входящих запросов.
- ngrok — туннель к localhost для получения вебхуков
- webhook.site — бесплатный эндпоинт для просмотра входящих запросов
- requestbin.com — аналог webhook.site
curl: швейцарский нож
curl — универсальный инструмент для отладки API из терминала. Полезные флаги:
# Полная информация: заголовки запроса и ответа
curl -v https://api.example.com/users
# Только заголовки ответа
curl -I https://api.example.com/users
# POST с JSON-телом
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-d '{"name": "Иван", "email": "ivan@test.ru"}'
# Вывести тело ответа + HTTP-статус
curl -s -o /dev/null -w "%{http_code}" https://api.example.com/health
# Таймаут подключения и ожидания
curl --connect-timeout 5 --max-time 30 https://api.example.com/slow-endpoint
# Следовать редиректам
curl -L https://api.example.com/redirect
# Игнорировать SSL (для тестирования с самоподписанным сертификатом)
curl -k https://localhost:8443/api
# Замер времени
curl -o /dev/null -s -w "
DNS: %{time_namelookup}s
Connect: %{time_connect}s
TLS: %{time_appconnect}s
FirstByte: %{time_starttransfer}s
Total: %{time_total}s
HTTP Code: %{http_code}
" https://api.example.com/users
curl -v: что означает вывод
* Connected to api.example.com (93.184.216.34) port 443
> POST /users HTTP/2 ← ваш запрос (>)
> Host: api.example.com
> Authorization: Bearer eyJhbG...
> Content-Type: application/json
>
< HTTP/2 201 ← ответ сервера (<)
< content-type: application/json
< x-request-id: req_abc123
<
{"id": 42, "name": "Иван"} ← тело ответа
> — заголовки запроса, < — заголовки ответа, * — служебная информация curl.
Логирование запросов
Добавьте логирование в ваш код — это сэкономит часы отладки:
JavaScript (Axios interceptors)
import axios from 'axios';
const api = axios.create({ baseURL: 'https://api.example.com' });
// Логирование запросов
api.interceptors.request.use(config => {
console.log(`→ ${config.method.toUpperCase()} ${config.url}`, {
headers: config.headers,
data: config.data
});
config.metadata = { startTime: Date.now() };
return config;
});
// Логирование ответов
api.interceptors.response.use(
response => {
const duration = Date.now() - response.config.metadata.startTime;
console.log(`← ${response.status} ${response.config.url} (${duration}ms)`, {
data: response.data
});
return response;
},
error => {
const duration = Date.now() - error.config.metadata.startTime;
console.error(`✕ ${error.response?.status || 'NETWORK'} ${error.config.url} (${duration}ms)`, {
data: error.response?.data,
message: error.message
});
return Promise.reject(error);
}
);
Python (requests + logging)
import requests
import logging
import time
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('api')
class APIClient:
def __init__(self, base_url, token):
self.base_url = base_url
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
})
def request(self, method, path, **kwargs):
url = f'{self.base_url}{path}'
logger.info(f'→ {method} {url}')
if 'json' in kwargs:
logger.debug(f' Body: {kwargs["json"]}')
start = time.time()
try:
response = self.session.request(method, url, **kwargs)
duration = (time.time() - start) * 1000
logger.info(f'← {response.status_code} {url} ({duration:.0f}ms)')
if not response.ok:
logger.error(f' Error: {response.text}')
response.raise_for_status()
return response.json()
except requests.exceptions.ConnectionError as e:
logger.error(f'✕ Connection failed: {e}')
raise
PHP (Guzzle middleware)
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Middleware;
use GuzzleHttp\MessageFormatter;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$logger = new Logger('api');
$logger->pushHandler(new StreamHandler('php://stderr', Logger::DEBUG));
$stack = \GuzzleHttp\HandlerStack::create();
$stack->push(
Middleware::log(
$logger,
new MessageFormatter(
"→ {method} {uri}\n← {code} ({res_header_Content-Type})\n{res_body}"
)
)
);
$client = new Client([
'base_uri' => 'https://api.example.com',
'handler' => $stack,
'timeout' => 10,
'headers' => [
'Authorization' => 'Bearer ' . $token,
'Content-Type' => 'application/json'
]
]);
Финальный чек-лист
✅ Быстрый чек-лист при ошибке API
| # | Проверка | Инструмент |
|---|---|---|
| 1 | HTTP-статус код ответа | DevTools, curl -v |
| 2 | Тело ответа (JSON с ошибкой) | DevTools, Postman |
| 3 | URL правильный (версия, путь, слэши) | Документация API |
| 4 | HTTP-метод правильный (GET/POST/PUT) | Swagger/OpenAPI |
| 5 | Content-Type заголовок | curl -v |
| 6 | Аутентификация (токен, формат) | jwt.io, curl |
| 7 | Тело запроса (валидный JSON, обязательные поля) | jq, JSONLint |
| 8 | CORS (только браузер, проверьте OPTIONS) | DevTools → Network |
| 9 | SSL-сертификат | openssl, curl -k |
| 10 | Сеть (DNS, firewall, VPN) | ping, traceroute, nslookup |
| 11 | Серверные логи (для своего API) | tail -f error.log |
| 12 | Минимизировать запрос | Postman, curl |
💡 Золотое правило отладки API
Если запрос работает через curl/Postman, но не из кода — проблема в вашем коде (заголовки, сериализация, CORS).
Если не работает и через curl — проблема на стороне API (сервер, сеть, аутентификация).
FAQ
❓ Какой первый шаг при отладке API-ошибки?
Ответ: Проверить HTTP-статус код ответа. Он подскажет категорию ошибки: 4xx — проблема на стороне клиента (неверный запрос, авторизация, не найден ресурс), 5xx — проблема на стороне сервера. Затем изучите тело ответа — большинство API возвращают JSON с описанием ошибки.
❓ Как отладить CORS-ошибку?
Ответ: CORS — это защита браузера, а не серверная ошибка. Шаги: 1) Откройте DevTools → Network и найдите OPTIONS preflight-запрос. 2) Проверьте заголовки Access-Control-Allow-Origin и Access-Control-Allow-Methods. 3) Воспроизведите через curl (curl не подвержен CORS). 4) Настройте сервер — подробнее в статье «5 способов исправить CORS».
❓ Чем Postman лучше curl для отладки API?
Ответ: Postman предоставляет GUI, сохранение коллекций, переменные окружения, авто-тесты, историю, генерацию кода. curl лучше для автоматизации, скриптов, CI/CD и быстрых проверок. Оптимально: Postman для исследования, curl для автоматизации и скриптов.
❓ Как отладить API, если сервер возвращает 500?
Ответ: 500 — ошибка на стороне сервера. Если это ваш API: 1) Проверьте серверные логи. 2) Воспроизведите через curl -v. 3) Убедитесь что БД и зависимости доступны. 4) Попробуйте минимальный запрос. Если чужой API: проверьте статус-страницу, попробуйте позже, свяжитесь с поддержкой.
Заключение
📝 Запомните главное
- Статус код → тело ответа → curl → документация — этот порядок решает 90% проблем
- Изолируйте проблему — отделите код от API, клиент от сервера, сеть от приложения
- Логируйте всё — добавляйте interceptors с таймингами и request_id
- CORS — это браузер, а не сервер. curl и Postman его не имеют
- Минимизируйте — уберите всё лишнее, найдите минимальный воспроизводимый запрос
Систематический подход к отладке превращает часы мучений в минуты диагностики. Сохраните этот чек-лист — он пригодится при следующей API-ошибке (а она обязательно будет).
🎯 Тестируйте API без ошибок
Нужен стабильный Mock API для отладки фронтенда? LightBox API создаёт моковые эндпоинты с предсказуемыми ответами — никаких 500 и таймаутов.
- ✓ Настраиваемые HTTP-статусы (200, 400, 404, 500)
- ✓ Задержка ответа для тестирования таймаутов
- ✓ CORS-заголовки из коробки
- ✓ Логирование всех входящих запросов
- ✓ Бесплатный план для старта
Статья опубликована: 23 февраля 2026
Автор: LightBox API Team