Ошибка 413 Request Entity Too Large при загрузке файлов через Kubernetes Ingress — типичная ситуация: приложение «умеет» принимать большие файлы, но один из прокси-слоёв по дороге режет запрос по лимиту client_max_body_size (или аналогу). В кластере слоёв часто несколько: внешний балансировщик, nginx ingress controller, сервисная сетка/sidecar-прокси, а дальше ещё и ваш backend (Nginx/Apache/Node/PHP/FastAPI и т. д.).
Ниже — практичный разбор: как понять, где именно возникает 413, какие настройки отвечают за лимит загрузки, какие аннотации Nginx Ingress использовать (включая nginx.ingress.kubernetes.io/proxy-body-size), и как проверить, что лимит увеличился там, где нужно.
Что означает 413 и почему в Kubernetes это «слоёный пирог»
413 Request Entity Too Large означает: сервер (или прокси перед ним) отказывается принимать тело запроса, потому что оно превышает допустимый размер. Для загрузок это почти всегда POST/PUT с большим Content-Length или chunked-upload.
Типовой маршрут в Kubernetes выглядит так:
Клиент → (Cloud LB / reverse proxy) → Ingress Controller → Service → Pod → (внутренний прокси/приложение)
Лимит может стоять на любом звене. Важно: если поднять лимит только на ingress-слое, но дальше (внутри pod’а) есть свой Nginx/Apache/приложение с меньшим лимитом, вы всё равно получите ошибку, просто «чуть позже» по цепочке.
Быстрая диагностика: кто именно отдаёт 413
1) Смотрим ответ и заголовки
Часто Nginx отдаёт типовую HTML-страницу ошибки и заголовок Server: nginx. Но это не доказательство, что виноват именно Ingress: Server может совпадать и у внешнего прокси, и у внутреннего Nginx в pod’е.
curl -i -X POST https://example.com/upload -F file=@big.iso
Если запрос слишком большой, вы увидите 413. Дальше — ищем, где он возник: по логам и по «наличию следов» запроса на каждом слое.
2) Проверяем логи Nginx Ingress Controller
Найдите namespace и pod контроллера (часто ingress-nginx), затем посмотрите логи за последние минуты:
kubectl -n ingress-nginx get pods
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller --since=10m
Если лимит сработал на ingress-слое, в логах Nginx обычно видно сообщение вида:
client intended to send too large body
Если в логах ingress «тишина», но клиент стабильно получает 413, вероятны два сценария: 413 возвращает внешний балансировщик/прокси перед кластером или backend (например, Nginx внутри pod’а).
3) Проверяем логи backend
Посмотрите логи pod’а приложения (или внутреннего веб-сервера, если он есть):
kubectl -n app get pods
kubectl -n app logs pod/my-app-7c9d6b6b6f-abcde --since=10m
Если у вас внутри pod’а Nginx, то 413 может быть именно от него — и тогда настройки Ingress сами по себе не помогут.

Самая частая причина: лимит в Nginx Ingress Controller
В Nginx лимит на размер тела запроса задаёт директива client_max_body_size. В мире Ingress её обычно настраивают аннотацией nginx.ingress.kubernetes.io/proxy-body-size.
Проверьте ваш Ingress и добавьте аннотацию на нужный хост/путь:
kubectl -n app get ingress my-app -o yaml
Пример Ingress (как текст). Обратите внимание на nginx.ingress.kubernetes.io/proxy-body-size:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
namespace: app
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
Значения задаются в единицах Nginx: k, m, g. Практика: начинайте с реалистичного предела (например, 20–100 МБ), а не с «безлимита».
Как убедиться, что аннотация реально применена
Ingress Nginx генерирует конфиг автоматически. После изменения Ingress контроллер должен подхватить обновления. Чтобы убедиться, что лимит попал в конфиг нужного виртуального хоста, можно вывести полный конфиг и найти свой server-блок по домену:
kubectl -n ingress-nginx exec -it deploy/ingress-nginx-controller -- nginx -T
В выводе проверьте, что для вашего хоста присутствует client_max_body_size с ожидаемым значением.
Глобальный лимит: ConfigMap контроллера против аннотаций
Иногда удобнее задавать лимит «по умолчанию» для всего контроллера, а аннотациями переопределять только исключения. Это делается через ConfigMap ingress-nginx.
Логика простая:
- ConfigMap — базовые значения «для всех».
- Аннотации — точечная настройка конкретного Ingress.
На практике лимит тела запроса чаще держат именно в аннотации nginx.ingress.kubernetes.io/proxy-body-size, потому что она привязана к конкретному приложению и снижает риск случайно «разрешить всем грузить всё».
Если у вас мульти-tenant кластер, избегайте глобального снятия ограничений: большие тела запросов увеличивают риск DoS по памяти/диску и делают нагрузку менее предсказуемой.
Не забудьте про backend: Nginx/Apache/приложение тоже может резать
Даже если Ingress пропускает 100 МБ, backend может вернуть ошибку на своей стороне. Дальше — самые частые места.
Если внутри pod’а тоже Nginx
Проверьте конфигурацию внутреннего Nginx: там тоже может быть client_max_body_size. Типовая ловушка: Ingress подняли до 50m, а внутри pod’а остался более низкий лимит.
Пример (как текст):
server {
listen 80;
client_max_body_size 50m;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
Если внутренний Nginx используется как sidecar/reverse-proxy — лимит нужно поднять и там.
Если backend — PHP-FPM
В PHP-стеке ограничения обычно задают upload_max_filesize и post_max_size. Иногда это проявляется не как 413, а как 400/500 (зависит от связки и обработки ошибок), поэтому важно смотреть логи приложения и PHP-FPM.
Совет по эксплуатации: согласуйте лимиты на всех уровнях, иначе можно получить частичные загрузки, обрывы и неочевидные ошибки в приложении.
Если backend — Node.js / Java / Python
У многих фреймворков и библиотек есть свои лимиты на body/multipart:
- Node.js/Express: ограничения middleware (например, для multipart).
- Spring Boot: multipart limits.
- Django/Flask/FastAPI: ограничения middleware/ASGI/WSGI и настройки сервера (gunicorn/uvicorn).
Поднимайте лимит синхронно: Ingress → внутренний прокси (если есть) → приложение.
Особый случай: 413 от внешнего балансировщика или прокси перед кластером
Если перед Kubernetes стоит внешний Nginx/HAProxy/балансировщик облака, 413 может прилетать ещё до Ingress. Типовые признаки:
- в логах ingress-controller нет следов запроса;
- в ответе видны заголовки/страница внешнего прокси;
- ошибка воспроизводится даже при обращении к другому Ingress.
Тогда лимит нужно поднимать и на внешнем уровне (для внешнего Nginx это снова client_max_body_size).
Проверка после изменений: как убедиться, что всё работает
1) Тестируем реальный размер
Сделайте тестовый файл нужного размера и отправьте через curl. Например, на Linux:
fallocate -l 60M test.bin
curl -i -X POST https://example.com/upload -F file=@test.bin
Если 413 ушла — это только половина успеха. Убедитесь, что загрузка действительно дошла до приложения и корректно обработалась: проверьте логи, итоговый объект/файл, checksum (если применимо).
2) Следим за таймаутами при больших файлах
Большие загрузки часто упираются не только в лимит размера, но и в таймауты (медленный канал, долгий TLS, перегруз upstream). Если после поднятия лимита вы начали видеть 504/499 — это уже другая ветка диагностики: таймауты ingress, таймауты upstream, буферизация и скорость чтения тела.
Если вы выбираете контроллер/архитектуру ingress под разные сценарии, полезно сравнить варианты в статье какой Ingress-контроллер выбрать: Traefik, Nginx или HAProxy.

Про безопасность и эксплуатацию: почему «поставить безлимит» — плохая идея
Сильно завышенный лимит (или попытка сделать «без ограничений») в Kubernetes почти всегда аукнется:
- ростом потребления памяти на ingress-нодах при параллельных загрузках;
- дополнительной нагрузкой на сеть и диски (если есть временные файлы/буферизация);
- сложностями с защитой от намеренных «заливок».
Практичный подход: ставьте лимит чуть выше реальной потребности, а для «тяжёлых» сценариев (видео, архивы, бэкапы) используйте отдельную архитектуру загрузки: прямую загрузку в объектное хранилище по pre-signed URL, chunked/resumable upload на уровне приложения, выделенный домен/ingress с отдельными политиками.
Чеклист: где проверять лимиты при 413
Внешний прокси/балансировщик перед кластером: есть ли там лимит тела запроса (для Nginx это
client_max_body_size).Nginx Ingress Controller: аннотация
nginx.ingress.kubernetes.io/proxy-body-sizeна Ingress (или дефолты через ConfigMap, если вы так стандартизируете).Внутренний reverse-proxy в pod’е (если есть):
client_max_body_size(или аналог в Apache/Envoy).Сам backend: лимиты multipart/body у фреймворка и рантайма.
Валидация: логи ingress + логи приложения + тестовый upload на нужный размер.
Типовые значения и практические рекомендации
Часто встречаемые ориентиры:
- админка CMS с загрузкой медиа: 20–100 МБ;
- импорт/экспорт данных: 50–200 МБ (но лучше делать асинхронно);
- крупные файлы: отдельный контур загрузки, а не «увеличить лимит везде».
Если вы ведёте несколько проектов в одном кластере, удобнее стандартизировать лимиты: базовый небольшой и точечные исключения аннотациями. Так Ingress не превращается в «воронку», куда можно залить что угодно.
Заключение
413 Request Entity Too Large в Kubernetes почти никогда не лечится «одной магической настройкой»: нужно точно определить слой, который режет запрос, и согласованно поднять лимит на этом слое и ниже по цепочке. Для Nginx Ingress ключевой инструмент — аннотация nginx.ingress.kubernetes.io/proxy-body-size, которая в итоге управляет client_max_body_size в сгенерированном конфиге.
Если помимо Ingress у вас есть внешний прокси, уделите внимание ещё и доменной/SSL-части (сертификаты, HSTS, миграции домена), чтобы избежать неожиданных обрывов при загрузках и редиректах: см. переезд домена: 301, HSTS и SSL без потерь. Для проектов в продакшене это часто всплывает одновременно с настройкой ingress.
Если проект крутится на своих серверах и вы строите кластер под себя, удобнее держать ingress и приложения на выделенных ресурсах, чтобы лимиты и нагрузка были предсказуемыми: для этого обычно берут VDS под Kubernetes-ноды и отдельные ingress-узлы.


