Ошибка 400 Bad Request в PHP: полный разбор и методы решения
Причины и варианты решения ошибки 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"