Ошибка 400 Bad Request в PHP: полный разбор и методы решения

Раздел: Веб-программирование PHP -> Ошибки HTTP

Причины и варианты решения ошибки 400 Bad Request

Ошибка 400 Bad Request (неверный запрос) возникает, когда сервер не может обработать запрос из-за синтаксической ошибки. В PHP-приложениях это часто связано с некорректными данными от клиента, проблемами с заголовками или настройками сервера. Рассмотрим основные подходы к устранению.

Как наиболее эффективно отловить и обработать причину 400 ошибки?

Основное решение: логирование входящих запросов и проверка структуры данных.

Первый шаг – записать полные данные запроса (метод, URI, заголовки, тело) в лог. Затем проанализировать, какая часть не соответствует ожиданиям сервера. Пример скрипта для логирования:

<?php
// log_request.php – включается в начале скрипта
$logFile = '/var/log/php_400_debug.log';
$data = "[" . date('Y-m-d H:i:s') . "] " .
  $_SERVER['REQUEST_METHOD'] . " " . $_SERVER['REQUEST_URI'] . "\n" .
  "Headers:\n" . print_r(getallheaders(), true) .
  "Body:\n" . file_get_contents('php://input') . "\n---\n";
file_put_contents($logFile, $data, FILE_APPEND | LOCK_EX);
?>

Php 400 bad request (ошибка 400 bad request в php)

Подключите этот файл в начале основного обработчика. После повторения ошибки проверьте лог. Если тело запроса содержит JSON, проверьте его валидность:

<?php
$rawBody = file_get_contents('php://input');
$json = json_decode($rawBody);
if (json_last_error() !== JSON_ERROR_NONE) {
  http_response_code(400);
  echo json_encode(['error' => 'Invalid JSON: ' . json_last_error_msg()]);
  exit;
}
?>

Php error 404 (ошибка 404 в php)

Возможные проблемы:

  • Недостаточно прав на запись в лог-файл (ошибка 500).
  • php://input доступен только для чтения один раз – убедитесь, что не вызываете его повторно.
  • Слишком большой размер запроса может привести к обрыву – установите лимиты в php.ini.

Как проверить корректность JSON в теле запроса?

Используйте функцию json_decode с проверкой ошибок. Дополнительно можно ограничить допустимые типы данных:

<?php
$data = json_decode(file_get_contents('php://input'), true);
if (json_last_error() !== JSON_ERROR_NONE) {
  http_response_code(400);
  exit('Invalid JSON format');
}
// Проверка обязательных полей
if (!isset($data['email'])) {
  http_response_code(400);
  exit('Missing required field: email');
}
?>

Проблема: при использовании Content-Type: application/x-www-form-urlencoded тело не распарсится через json_decode. Убедитесь, что заголовок установлен правильно.

Как настроить лимит размера тела запроса в PHP?

Если запрос слишком большой (например, загрузка файла), сервер может отдать 400. В php.ini настройте:

; Максимальный размер POST данных
post_max_size = 20M
; Максимальный размер загружаемого файла
upload_max_filesize = 10M
; Максимальное количество входных переменных
max_input_vars = 2000

После изменений перезапустите веб-сервер. Если изменения не видны, проверьте, не переопределены ли они в .htaccess или конфигурации виртуального хоста.

Типичная ошибка: превышение max_input_vars приводит к молчаливому обрезанию данных и 400. Увеличивайте этот параметр осознанно.

Как обрабатывать ошибки парсинга запроса при работе с формами?

Для традиционных форм проверяйте, что все обязательные поля присутствуют:

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $required = ['username', 'password'];
  foreach ($required as $field) {
    if (!isset($_POST[$field]) || trim($_POST[$field]) === '') {
      http_response_code(400);
      echo 'Missing field: ' . $field;
      exit;
    }
  }
  // дальнейшая обработка
}
?>

Для загрузки файлов также проверяйте код ошибки массива $_FILES:

<?php
if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
  $errorMessages = [
    UPLOAD_ERR_INI_SIZE   => 'File exceeds server limit',
    UPLOAD_ERR_FORM_SIZE  => 'File exceeds form limit',
    UPLOAD_ERR_PARTIAL    => 'File was partially uploaded',
    UPLOAD_ERR_NO_FILE    => 'No file uploaded'
  ];
  http_response_code(400);
  echo $errorMessages[$_FILES['file']['error']] ?? 'Unknown upload error';
  exit;
}
?>

Проблема: PHP может вернуть 400 из-за превышения upload_max_filesize до того, как скрипт начнёт выполнение. Логируйте такие случаи через обработчик ошибок.

Как проверить заголовки запроса, вызывающие 400?

Некорректные заголовки (например, неправильный Content-Type, кривой Host) могут привести к 400 на уровне веб-сервера. Используйте инструменты типа curl -v или браузерные инструменты разработчика. В PHP можно прочитать заголовки через функцию getallheaders() и проверить их:

<?php
$headers = getallheaders();
if (!isset($headers['Content-Type']) || strpos($headers['Content-Type'], 'application/json') === false) {
  http_response_code(400);
  echo 'Expected Content-Type: application/json';
  exit;
}
?>

Ошибка: некоторые серверы (например, Nginx) могут отклонять запросы с длинными URI (более 8KB). Настройте large_client_header_buffers в конфигурации сервера.

Как использовать отладку с помощью инструментов разработчика?

Откройте вкладку Network в браузере, найдите запрос с 400, посмотрите Request Headers, Request Payload. Сравните с ожидаемым форматом. Если используете Postman или cURL, отправьте тот же запрос и изменяйте параметры до успеха.

Дополнительно: в Apache можно настроить кастомную страницу для 400 ошибок через .htaccess:

ErrorDocument 400 /custom_400.php

В custom_400.php можно логировать запросы, используя $_SERVER переменные.

Расширенные примеры и практические сценарии

Пример 1: Полный обработчик с логированием и разбором JSON

Пример
<?php
// api_handler.php
// Включаем строгий режим и сохраняем тело запроса
$rawBody = file_get_contents('php://input');
$logEntry = [
  'time' => date('Y-m-d H:i:s'),
  'method' => $_SERVER['REQUEST_METHOD'],
  'uri' => $_SERVER['REQUEST_URI'],
  'headers' => getallheaders(),
  'body' => $rawBody
];
file_put_contents('/var/log/api_requests.log', json_encode($logEntry) . "\n", FILE_APPEND);

if (empty($rawBody)) {
  http_response_code(400);
  echo json_encode(['error' => 'Empty request body']);
  exit;
}

$data = json_decode($rawBody, true);
if (json_last_error() !== JSON_ERROR_NONE) {
  http_response_code(400);
  echo json_encode([
    'error' => 'Invalid JSON',
    'detail' => json_last_error_msg()
  ]);
  exit;
}

// Проверка обязательных полей
$required = ['name', 'email'];
$missing = array_diff($required, array_keys($data));
if (!empty($missing)) {
  http_response_code(400);
  echo json_encode(['error' => 'Missing fields', 'fields' => array_values($missing)]);
  exit;
}

// Успешная обработка
http_response_code(200);
echo json_encode(['status' => 'ok']);
?>
Пример вывода при неверном JSON:
{"error":"Invalid JSON","detail":"Syntax error"}

Пример 2: Отладка с помощью cURL для воспроизведения 400

Пример
# Отправить корректный запрос
curl -X POST https://example.com/api \
  -H "Content-Type: application/json" \
  -d '{"name":"test","email":"test@test.com"}'

# Отправить запрос с неверным JSON
curl -X POST https://example.com/api \
  -H "Content-Type: application/json" \
  -d '{name: test}'

# Отправить запрос без обязательного заголовка
curl -X POST https://example.com/api \
  -d '{"name":"test"}'
Результат для последнего (без Content-Type):
{"error":"Expected Content-Type: application/json"}
(если сервер проверяет заголовок)

Пример 3: Настройка Nginx для корректной обработки больших заголовков

Пример
# /etc/nginx/nginx.conf (в секции http)
proxy_buffers 8 16k;
proxy_buffer_size 32k;
large_client_header_buffers 4 16k;
client_max_body_size 20M;
client_header_buffer_size 4k;

Пояснение: параметр large_client_header_buffers предотвращает 400 при URI длиннее 8КБ (по умолчанию).

Пример 4: Обработка 400 на стороне клиента (JavaScript)

Пример
fetch('/api', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'test' })
}).then(response => {
  if (response.status === 400) {
    response.json().then(err => {
      console.error('400 Bad Request:', err);
      // показать пользователю сообщение
    });
  }
});
В консоли: 400 Bad Request: {error: "Invalid JSON"}

Пример 5: Регистрация ошибки 400 в базе данных с деталями

Пример
<?php
function logBadRequest($errorDetail) {
  $pdo = new PDO('mysql:host=localhost;dbname=errors', 'user', 'pass');
  $stmt = $pdo->prepare("INSERT INTO http_errors (status, url, body, error, ip, time) VALUES (?, ?, ?, ?, ?, NOW())");
  $stmt->execute([
    400,
    $_SERVER['REQUEST_URI'],
    file_get_contents('php://input'),
    $errorDetail,
    $_SERVER['REMOTE_ADDR']
  ]);
}

// Использование:
$raw = file_get_contents('php://input');
$json = json_decode($raw);
if (!$json) {
  logBadRequest('Invalid JSON: ' . json_last_error_msg());
  http_response_code(400);
  exit;
}
?>

Пример 6: Проверка и фильтрация Upload с помощью проверок MIME-типа и размера

Пример
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['document'])) {
  $file = $_FILES['document'];
  $allowedMimes = ['application/pdf', 'image/png'];
  $finfo = finfo_open(FILEINFO_MIME_TYPE);
  $mime = finfo_file($finfo, $file['tmp_name']);
  finfo_close($finfo);

  if (!in_array($mime, $allowedMimes)) {
    http_response_code(400);
    exit('Invalid file type');
  }
  if ($file['size'] > 5 * 1024 * 1024) {
    http_response_code(400);
    exit('File too large (max 5MB)');
  }
  // обработка
}
?>
Если загружен .exe: HTTP 400 с текстом "Invalid file type"

ошибка 400 Bad Request в PHP - comments

En
Php 400 bad request (php)