웹 서비스를 운영하다가 갑자기 브라우저에 504 Gateway Timeout 에러 화면이 뜨면서 요청이 멈춰버리는 현상을 경험해봤을 것이다.
다만 대부분의 개발자들은 이 에러가 발생했을 때 백엔드 서버가 다운된 것인지, Nginx 설정 문제인지 정확한 원인을 찾지 못해 무작정 서버를 재부팅하거나 타임아웃 숫자만 아무렇게나 올리곤 한다.
이번에는 504 Gateway Timeout 에러가 정확히 왜 발생하는지 원인을 분석하고, Nginx와 PHP-FPM 연동 환경에서 타임아웃을 안전하게 설정하여 문제를 완전히 해결하는 방법을 정리해서 소개하겠다.
504 Gateway Timeout은 게이트웨이 역할을 하는 웹 서버(Nginx, Apache 등)가 업스트림 서버(PHP-FPM, Node.js, Python WSGI 등)로부터 지정된 시간 내에 응답을 받지 못했을 때 발생하는 HTTP 상태 코드다.
클라이언트의 요청 처리 흐름을 순서대로 살펴보면 구조를 쉽게 이해할 수 있다.
| 단계 | 주체 | 동작 및 상태 |
|---|---|---|
| 1단계 | 브라우저(Client) | Nginx 웹 서버로 대용량 작업 또는 장시간 API 요청 전송 |
| 2단계 | Nginx (Reverse Proxy) | 요청을 받아 PHP-FPM(Upstream Server)으로 전달 후 응답 대기 |
| 3단계 | PHP-FPM (Application) | 복잡한 연산, 외부 API 통신, 대량 DB 쿼리 실행 중 (시간 지연) |
| 4단계 | Nginx (Timeout 발생) | Nginx에 설정된 대기 시간(기본 60초) 초과 시 504 에러 반환 |
- 대용량 엑셀 다운로드, 이미지 처리 등 오래 걸리는 백엔드 작업
- 외부 API 호출 시 응답 지연 또는 타임아웃 미설정으로 인한 블로킹
- DB 인덱스 누락으로 인한 롱 쿼리(Slow Query) 발생
- Nginx 및 PHP-FPM의 타임아웃 설정값 불일치
Nginx 환경에서 PHP-FPM을 사용할 때 504 에러를 해결하려면 Nginx의 FastCGI 타임아웃과 PHP-FPM의 실행 시간을 함께 조정해야 한다.
어느 한쪽만 늘려주면 다른 쪽에서 여전히 타임아웃을 걸어 에러가 계속 발생한다.
Nginx의 location ~ \.php$ 블록 내부 또는 http 블록에 FastCGI 관련 타임아웃 옵션을 추가한다.
http {
# FastCGI 서버로부터 응답을 읽는 타임아웃 (초 단위)
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
fastcgi_connect_timeout 300;
}
Nginx 대기 시간을 늘려도 PHP 자제 실행 시간이 초과되면 500 Internal Server Error 또는 504 에러가 발생하므로 PHP 설정도 함께 맞춰주어야 한다.
; php.ini 파일 설정
max_execution_time = 300
max_input_time = 300
특정 작업(예: 대용량 데이터 배치 처리 API)에서만 응답 시간이 길어지는 경우, 전체 서버의 타임아웃을 무작정 늘리는 것보다 특정 경로에만 타임아웃을 별도로 부여하거나 스크립트 상에서 처리하는 것이 안전하다.
✗ Nginx 전체 설정에서 타임아웃을 3600초(1시간)처럼 과도하게 높여두면, 무한 루프나 DB 락 현상이 발생했을 때 프로세스가 종료되지 않고 서버 메모리를 계속 점유하여 전체 서비스가 마비된다.
# 잘못된 방식: 모든 요청에 대해 과도한 타임아웃 설정
http {
fastcgi_read_timeout 3600; # 1시간 대기 (위험)
}
✓ 대용량 처리가 필요한 특정 위치(Location)에만 타임아웃을 분리 설정하고, PHP 스크립트 내에서도 실행 타임아웃을 명시적으로 제어한다.
# Nginx vhost 설정
location /admin/export-excel.php {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# 특정 장시간 작업 전용 타임아웃 설정
fastcgi_read_timeout 300;
}<?php
// export-excel.php 상단
// 해당 스크립트에 한해 최대 실행 시간을 300초로 연장
set_time_limit(300);
// 메모리 제한 설정
ini_set('memory_limit', '512M');
// 대용량 데이터 처리 로직 실행
// ...
echo json_encode(['status' => 'success', 'message' => 'Export completed']);
?>
타임아웃 설정 변경 시 실무에서 흔히 저지르는 실수와 대처 가이드를 확인해보자.
- Nginx 재로드 누락: 설정 파일 수정 후
nginx -t로 구문 검사를 하고systemctl reload nginx를 실행해야 반영된다. - 동기식 대용량 처리의 한계: 3~5분이 넘어가는 작업은 웹 요청(HTTP)으로 동기 처리하기보다 RabbitMQ, Redis Queue 등의 메시지 큐를 이용해 백그라운드 비동기 작업(Worker)으로 전환하는 것이 정석이다.
- Cloudflare/AWS ALB 타임아웃: Nginx 앞단에 Cloudflare나 AWS ALB(Application Load Balancer) 같은 리버스 프록시/CDN이 있는 경우, 해당 서비스의 자체 타임아웃(Cloudflare 기본 100초)에 먼저 걸려 504 에러가 반환될 수 있다.
Nginx 504 Gateway Timeout 에러는 게이트웨이와 백엔드 애플리케이션 간의 응답 대기 시간 불일치 및 백엔드 병목에서 주로 발생한다.
- Nginx의
fastcgi_read_timeout옵션 설정 - PHP의
max_execution_time및set_time_limit()조정 - 장시간 소요 작업은 특정 경로 분리 또는 비동기 큐 방식으로 구조 개선
504 Gateway Timeout 에러 해결은 단순한 숫자 수정이 아니라 시스템 병목을 줄이는 첫걸음이다. 무작정 대기 시간만 늘리는 임시방편 대신 서비스 구조에 맞는 적절한 타임아웃 값을 설정하고 로직을 최적화하여 쾌적하고 안정적인 웹 서비스를 구축해보자.