Python Requests headers: заголовки для GET, POST и User-Agent
28 авг. 2026 г. · Руководства · 9 мин. чтения
В requests HTTP-заголовки передают через словарь в аргументе headers методов requests.get(), requests.post() и других вызовов. Общие настройки для серии запросов удобнее задать в Session через session.headers.update(). Ниже разберем рабочие примеры для GET и POST, настройку User-Agent, приоритеты заголовков, диагностику ответа 403 и подключение прокси для разрешенных задач.
Материал рассчитан на разработчиков API-клиентов, тестовых скриптов и систем сбора открытых данных, работающих в рамках правил целевого ресурса. Примеры используют локальный сервер: их можно повторить без отправки данных на сторонний сайт.
Коротко
Для одного запроса передайте словарь в
headers, напримерrequests.get(url, headers={"Accept": "application/json"}). Для постоянных значений используйтеsession.headers.update(...). Проверяйте фактически подготовленный запрос черезresponse.request.headers, а заголовки ответа читайте изresponse.headers.
Что такое headers в Python Requests
HTTP-заголовки представляют собой пары «имя-значение», сопровождающие запрос или ответ. В них передают формат данных, сведения о клиенте, параметры кеширования и данные аутентификации. В Python Requests исходящие заголовки обычно задают словарем в headers=, а ответ сервера доступен в response.headers.
На практике чаще всего нужны Accept, Content-Type, Authorization и User-Agent. Корректные значения помогают серверу правильно обработать вызов и делают ошибки API понятнее. Для одиночного вызова достаточно headers=; для нескольких связанных запросов используйте requests.Session.
Подготовка окружения
Установка зависимостей
Создайте виртуальное окружение и установите проверенные версии пакетов:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install requests==2.34.2 PySocks==1.7.1
PySocks нужен только для примера с SOCKS-прокси. Для остальных фрагментов достаточно Requests.
| Компонент | Версия | Назначение |
|---|---|---|
| Python | 3.14.0 | Запуск примеров |
| Requests | 2.34.2 | Отправка и просмотр HTTP-запросов |
| PySocks | 1.7.1 | Поддержка адресов вида socks5h:// |
| Тестовая цель | Локальный HTTP echo-сервер | Воспроизводимая проверка без внешнего сервиса |

Рисунок 1. Перед запуском примеров проверьте версии Python и Requests.
Локальный echo-сервер
Сохраните следующий код как code/local_echo_server.py. Он принимает запросы на 127.0.0.1, возвращает полученные заголовки и тело, а для пути /blocked намеренно отвечает 403.
import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class Handler(BaseHTTPRequestHandler):
def reply(self, status=200):
length = int(self.headers.get("Content-Length", "0"))
raw_body = self.rfile.read(length) if length else b""
parsed_json = None
body_text = None
if raw_body:
body_text = raw_body.decode("utf-8", errors="replace")
try:
parsed_json = json.loads(raw_body)
except json.JSONDecodeError:
pass
payload = {
"method": self.command,
"path": self.path,
"headers": dict(self.headers),
"json": parsed_json,
"body_text": body_text,
}
body = json.dumps(payload, indent=2).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_GET(self):
self.reply(403 if self.path == "/blocked" else 200)
def do_POST(self):
self.reply()
def log_message(self, format, *args):
return
server = ThreadingHTTPServer(("127.0.0.1", 8765), Handler)
print("Local test server: http://127.0.0.1:8765", flush=True)
print("Press Control-C to stop.", flush=True)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
finally:
server.server_close()
В первом окне терминала запустите сервер и не закрывайте его до окончания тестов:
python3 code/local_echo_server.py

Рисунок 2. Локальный сервер слушает порт 8765 и готов принимать клиентские запросы.
Как задать заголовки в Python Requests
Создаем словарь headers
Заголовок несет метаданные до тела запроса: допустимый формат ответа, тип отправляемого тела, параметры аутентификации или сведения о программе. В Requests это обычный словарь:
import requests
url = "http://127.0.0.1:8765/headers"
headers = {
"User-Agent": "inventory-client/2.1",
"Accept": "application/json",
}
response = requests.get(url, headers=headers, timeout=(2, 5))
response.raise_for_status()
print(response.status_code)
Имена HTTP-заголовков регистронезависимы, но единый стиль записи облегчает чтение кода. Значения должны быть строками. Content-Length вручную лучше не указывать: Requests вычислит его по закодированному телу. Помните и о специальных аргументах: auth= или учетные данные в URL прокси могут иметь приоритет над значениями из headers.
Headers для GET-запроса
Ниже минимальный, но полноценный вариант GET-запроса с заголовками:
import requests
url = "http://127.0.0.1:8765/headers"
headers = {
"User-Agent": "inventory-client/2.1 (+https://rola-ip.co/)",
"Accept": "application/json",
"X-Trace-ID": "items-read-001",
}
response = requests.get(url, headers=headers, timeout=(2, 5))
response.raise_for_status()
received = response.json()
print("Status:", response.status_code)
print("Method:", received["method"])
print("User-Agent:", received["headers"]["User-Agent"])
print("X-Trace-ID:", received["headers"]["X-Trace-ID"])
Accept сообщает о предпочтительном формате ответа, но не заставляет сервер вернуть JSON. Вызывайте response.json() после успешного ответа и, для нестабильного API, после проверки Content-Type.
Задавайте timeout всегда. Первое число ограничивает ожидание подключения, второе – паузу между порциями данных ответа. Эта пара не означает, что вся загрузка завершится за их сумму.

Рисунок 3. Echo-сервер получил заголовки конкретного запроса и вернул HTTP 200.
Как установить User-Agent
По умолчанию Requests использует User-Agent библиотеки, например python-requests/2.34.2. Если API просит идентифицировать клиент, задайте понятное имя приложения:
import requests
response = requests.get(
"http://127.0.0.1:8765/headers",
headers={"User-Agent": "inventory-client/2.1 (+https://rola-ip.co/)"},
timeout=(2, 5),
)
response.raise_for_status()
print(response.json()["headers"]["User-Agent"])
Название продукта, версия и страница с контактами полезнее оператору API, чем строка браузера, скопированная из другого клиента. Если сервис документирует свой формат User-Agent, соблюдайте именно его.
Подмена User-Agent не превращает Requests в Chrome: библиотека не исполняет JavaScript, не воспроизводит TLS-профиль браузера, client hints и сценарии аутентификации. Поэтому User-Agent – средство идентификации, а не универсальное решение для 403 Forbidden.
Headers в POST: JSON, формы и Content-Type
POST с JSON-телом
Для JSON API передавайте объект Python через json=:
import requests
url = "http://127.0.0.1:8765/items"
headers = {
"User-Agent": "inventory-client/2.1 (+https://rola-ip.co/)",
"Accept": "application/json",
"X-Trace-ID": "item-create-001",
}
payload = {"name": "monitor", "quantity": 2}
response = requests.post(url, headers=headers, json=payload, timeout=(2, 5))
response.raise_for_status()
received = response.json()
print("Status:", response.status_code)
print("Method:", received["method"])
print("User-Agent:", received["headers"]["User-Agent"])
print("Content-Type:", received["headers"]["Content-Type"])
print("JSON received:", received["json"])
json=payload сериализует объект и добавляет подходящий JSON Content-Type. Не вызывайте сначала json.dumps(), а затем не передавайте строку в json=: сервер получит JSON-строку вместо исходной структуры.
data= выбирают, когда контракт API требует полей формы или уже закодированного тела. Верный вариант определяется контрактом конечной точки, а не только методом HTTP.
| Сервер ожидает | Аргумент Requests | Обычный Content-Type |
|---|---|---|
| JSON-объект | json={"key": "value"} |
application/json |
| Поля HTML-формы | data={"key": "value"} |
application/x-www-form-urlencoded |
| Байты или текст | data=encoded_body |
Укажите согласно контракту API |
| Загрузку файла | files={...} |
multipart/form-data; boundary=... |

Рисунок 4. POST-запрос передал User-Agent, заголовок трассировки, Content-Type и JSON-объект.
Session и проверка фактических headers
Повторное использование заголовков с Session
requests.Session хранит заголовки и cookies между вызовами, а также переиспользует соединения с одним хостом. Это удобно для небольшого API-клиента или последовательной разрешенной обработки страниц:
import requests
with requests.Session() as session:
session.headers.update({
"User-Agent": "inventory-client/2.1",
"Accept": "application/json",
})
response = session.get(
"http://127.0.0.1:8765/headers",
headers={"X-Trace-ID": "session-001"},
timeout=(2, 5),
)
response.raise_for_status()
received = response.json()
print("Status:", response.status_code)
print("User-Agent:", received["headers"]["User-Agent"])
print("X-Trace-ID:", received["headers"]["X-Trace-ID"])
Requests объединяет заголовки конкретного запроса со значениями Session. При совпадении ключа побеждает значение вызова. Не передавайте изменяемый Session нескольким потокам без синхронизации: cookies и другое состояние станут непредсказуемыми.

Рисунок 5. User-Agent из Session и X-Trace-ID конкретного запроса попали в одно сообщение.
Как посмотреть отправленные и полученные заголовки
Проверяйте подготовленный запрос вместо предположения, что исходный словарь ушел без изменений:
import requests
response = requests.get(
"http://127.0.0.1:8765/headers",
headers={"User-Agent": "inspection-client/1.0"},
timeout=(2, 5),
)
response.raise_for_status()
print("Method:", response.request.method)
print("URL:", response.request.url)
print("User-Agent sent:", response.request.headers["User-Agent"])
print("Response Content-Type:", response.headers["Content-Type"])
| Выражение | Что содержит |
|---|---|
response.request.headers |
Заголовки, отправленные клиентом |
response.headers |
Заголовки, возвращенные сервером |
session.headers |
Значения по умолчанию для Session |
response.history |
Предыдущие ответы при редиректах |
Не выводите в production-лог все заголовки целиком. В Authorization, Cookie, Proxy-Authorization, API-ключах и пользовательских токенах могут оказаться секреты. Логируйте только безопасные поля или маскируйте их значения.

Рисунок 6. Скрипт отдельно показывает headers, отправленные Requests, и headers ответа сервера.
Приоритеты и редиректы
Итоговый запрос формируется из нескольких источников. Важны три правила:
- Если схема поддерживается библиотекой, используйте
auth=вместо ручной сборкиAuthorization. Данные из.netrcиauth=могут переопределить одноименный ключ вheaders. - Учетные данные прокси передавайте в URL прокси или по документированному провайдером способу. Requests может заменить заданный вручную
Proxy-Authorization. - Не задавайте
Content-Length: если длина тела известна, Requests рассчитает или заменит значение сам.
При редиректе на другой хост Requests удаляет Authorization из соображений безопасности. Это не значит, что первоначальный словарь проигнорирован: при отладке сверяйте response.history и подготовленные headers на финальном URL.
Ошибки headers: диагностика до изменения кода
Что проверить при 400, 401, 403, 415 и 429
Сначала соберите статус, тело ответа, финальный URL, историю редиректов и безопасную часть response.request.headers. Добавление набора «браузерных» заголовков к каждому неудачному запросу обычно маскирует причину.
| Симптом | Вероятная причина | Как проверить | Практическое действие |
|---|---|---|---|
400 Bad Request |
Некорректный синтаксис, заголовок или структура тела | Прочитать ошибку API и сверить схему | Исправить поле или кодировку |
401 Unauthorized |
Нет, истекли или были переопределены учетные данные | Проверить схему auth и безопасные метаданные токена | Обновить данные; при поддержке использовать auth= |
403 Forbidden |
Права, политика, лимит, репутация IP или контроль автоматизации | Сверить права, лимиты, ответ и разрешенный сетевой путь | Запросить доступ, снизить частоту или использовать согласованный маршрут |
406 Not Acceptable |
Неподдерживаемый Accept |
Проверить доступные форматы ответа | Передать документированный media type |
415 Unsupported Media Type |
Тело не соответствует Content-Type |
Проверить подготовленные тело и headers вместе | Правильно выбрать json=, data= или files= |
429 Too Many Requests |
Превышен rate limit | Проверить Retry-After и лимиты провайдера |
Уменьшить параллелизм, сделать backoff с jitter |
| Таймаут соединения/чтения | Сеть, прокси или медленный upstream | Раздельно проверить DNS, прямой путь и прокси | Ограничить таймауты и повторять лишь безопасные операции |
При 403 сначала проверьте URL, авторизацию, правила доступа и частоту запросов. Свой User-Agent может быть нужен для идентификации клиента, но сам по себе не дает права доступа. Случайно добавленные Referer, скопированные cookies или Sec-CH-UA-* нередко делают запрос менее согласованным.
Повторные попытки тоже требуют контекста. GET часто можно повторить, а автоматический повтор POST способен создать дубль, если API не поддерживает ключ идемпотентности. Учитывайте Retry-After и ограничивайте экспоненциальную задержку.
Следующий код воспроизводит контролируемый 403, не вызывая raise_for_status() раньше времени:
import requests
response = requests.get("http://127.0.0.1:8765/blocked", timeout=(2, 5))
print("Status:", response.status_code)
print("URL:", response.request.url)
print("User-Agent sent:", response.request.headers["User-Agent"])
if response.status_code == 403:
print("Diagnosis: controlled 403 reproduced; inspect policy and permissions.")

Рисунок 7. Демонстрация намеренно получает 403 и показывает подготовленный User-Agent до дальнейшей диагностики.
Прокси в Requests: отделяйте сетевой маршрут от headers
Если разрешенный запрос работает из одной сети, но постоянно отклоняется с общего серверного IP, причина может быть в репутации адреса, геолокации или лимите на IP, а не в словаре headers. Аутентифицированный прокси меняет сетевой маршрут, тогда как заголовки описывают само HTTP-сообщение.
Rola IP предлагает резидентские прокси, динамические дата-центровые прокси и мобильные прокси. Дата-центровый маршрут может подойти для стабильных, чувствительных к скорости API-вызовов; резидентский и мобильный выбирают с учетом разрешенных правил доступа, требуемой географии и объема работы. Для сценариев на Python используйте инструкцию по подключению прокси в Python.
В разделе Proxy Setup панели Rola IP выберите регион, протокол, способ аутентификации и аккаунт. Хост, порт, логин и пароль храните в переменных окружения или менеджере секретов, а не в исходном коде. Порядок первичного подключения описан в руководстве по быстрому запуску прокси.

Рисунок 8. В Proxy Setup доступны хост, порт и параметры аутентификации для подключения.
Если нескольким вызовам нужен один выходной IP, выбирайте закрепленную сессию; когда отдельным допустимым запросам требуются разные выходы – ротацию на запрос. Параметры локации направляют трафик через нужную страну или регион. Новый IP не исправит неверный Content-Type или аутентификацию, как и другой User-Agent не устранит ограничение по репутации IP или географии.
Прокси не заменяет авторизацию, не отменяет условия использования, требования privacy, robots-директивы там, где они применимы, и rate limits. Он также не исполняет JavaScript. Браузерную автоматизацию применяют только когда страница действительно требует браузерного поведения и такой сценарий разрешен.
Рекомендации для Python Requests headers
- Указывайте правдивый и конкретный User-Agent, когда сервис просит идентифицировать клиента.
- Согласуйте
Acceptи способ кодирования тела с документацией API. - Для JSON используйте
json=, а не ручную сборку тела и длины. - Ограничивайте время и подключения, и чтения.
- Перед
raise_for_status()сохраните нужное для диагностики тело ошибки. - При отладке проверяйте
response.request.headers, скрывая секреты. - Используйте Session для связанных последовательных запросов.
- Повторяйте только подходящие методы, соблюдайте
Retry-Afterи ограничивайте backoff. - Храните токены и данные прокси вне исходного кода.
- Соблюдайте правила доступа, конфиденциальности и опубликованные лимиты целевого ресурса.
Итоги
Для разового вызова передавайте словарь headers, а общие настройки выносите в Session. Дайте Requests самостоятельно сериализовать JSON и рассчитать заголовки, связанные с телом, затем проверяйте подготовленный запрос при неожиданном результате. Когда разрешенная задача упирается в репутацию сети или географию, а не в headers, проблему следует рассматривать на уровне сетевого маршрута. В любом случае нужны таймауты, контролируемая частота запросов и аккуратное хранение секретов.