Как в nginx отдавать ошибки в виде json

При разработке API часто бывает так, что абсолютно все ответы даже с непредвиденными ошибками нужно отдавать в json-формате. В плане PHP, ошибки и эксепшены перехватывать достаточно легко. А вот 500, 502, 504, 404 и прочие ошибки могут генерироваться уже не на стороне PHP, а на стороне веб-сервера, например, nginx. В таком случае нужно править конфигурацию веб-сервера. Можно создать отдельный файл errors.conf:

error_page 500 =200 /500.json;
location /500.json {
    return 500 '{"error": {"status_code": 500,"status": "Internal Server Error"}}';
}
error_page 502 =200 /502.json;
location /502.json {
    return 502 '{"error": {"status_code": 502,"status": "Bad Gateway"}}';
}
error_page 503 =200 /503.json;
location /503.json {
    return 503 '{"error": {"status_code": 503,"status": "Service Temporarily Unavailable"}}';
}
error_page 504 =200 /504.json;
location /504.json {
    return 504 '{"error": {"status_code": 504,"status": "Gateway Timeout"}}';
}
error_page 400 =200 /400.json;
location /400.json {
    return 400 '{"error": {"status_code": 400,"status": "Bad Request"}}';
}
error_page 401 =200 /401.json;
location /401.json {
    return 401 '{"error": {"status_code": 401,"status": "Unauthorized"}}';
}
error_page 403 =200 /403.json;
location /403.json {
    return 403 '{"error": {"status_code": 403,"status": "Forbidden"}}';
}
error_page 404 =200 /404.json;
location /404.json {
    return 404 '{"error": {"status_code": 404,"status": "Not Found"}}';
}
error_page 408 =200 /408.json;
location /408.json {
    return 408 '{"error": {"status_code": 408,"status": "Request Timeout}}';
}
error_page 418 =200 /418.json;
location /418.json {
    return 418 '{"error": {"status_code": 418,"status": "I\'m a teapot"}}';
}

А затем в добавить в конфигурацию нужного домена строку:

include errors.conf;

Можете кстати попробовать добавить это в контексте всех http серверов, судя по документации это не запрещено.

Кратко как это работает. Директива error_page имеет такой синтаксис:

error_page код ... [=[ответ]] uri;

Т.е. для указанного кода ответа можно переопределить сам код ответа, например, возвращать всегда код 200, а код ошибки сообщать уже в json-ответе. Также нужно указать uri с содержимым ответа. Это может быть как файл на диске, так и внутренний локейшен. Чем, собственно, и воспользуемся.

В объявлении локейшена нет ничего необычного, поэтому сразу рассмотрим его содержимое. Директива return может быть описана тремя способами:

return код [текст];
return код URL;
return URL;

В данном случае нас интересует только первый вариант. При указании любого кода, за исключением кодов 301, 302, 303, 307 и 308 можно задать тела ответа, которое будет отправлено клиенту. Также в тексте тела ответа можно использовать переменные.

Ещё один способ отдавать ошибки nginx в json-формате

Можно воспользоваться другим вариантом и хранить тексты ответов в отдельных файлах.

    error_page 511 /errors/511.json;
    error_page 507 /errors/507.json;
    error_page 506 /errors/506.json;
    error_page 451 /errors/451.json;
    error_page 431 /errors/431.json;
    error_page 429 /errors/429.json;
    error_page 428 /errors/428.json;
    error_page 424 /errors/424.json;
    error_page 423 /errors/423.json;
    error_page 422 /errors/422.json;
    error_page 505 /errors/505.json;
    error_page 504 /errors/504.json;
    error_page 503 /errors/503.json;
    error_page 502 /errors/502.json;
    error_page 501 /errors/501.json;
    error_page 500 /errors/500.json;
    error_page 426 /errors/426.json;
    error_page 417 /errors/417.json;
    error_page 416 /errors/416.json;
    error_page 415 /errors/415.json;
    error_page 414 /errors/414.json;
    error_page 413 /errors/413.json;
    error_page 412 /errors/412.json;
    error_page 411 /errors/411.json;
    error_page 410 /errors/410.json;
    error_page 409 /errors/409.json;
    error_page 408 /errors/408.json;
    error_page 407 /errors/407.json;
    error_page 406 /errors/406.json;
    error_page 405 /errors/405.json;
    error_page 404 /errors/404.json;
    error_page 403 /errors/403.json;
    error_page 402 /errors/402.json;
    error_page 401 /errors/401.json;
    error_page 400 /errors/400.json;
    location ^~ /errors/ {
        internal;
        root <PATH TO PARENT FOLDER OF ERRORS FOLDER>;
    }