504 Gateway Timeout: 의미와 해결 방법
· 읽는 데 2분
오류의 의미
504는 앞단의 웹 서버(nginx, Apache, 로드 밸런서, CDN)가 뒤에 있는 애플리케이션을 기다리다 포기한 것입니다. 요청은 전달되었고 백엔드는 작업을 시작했지만, 응답이 돌아오기 전에 앞단의 타이머가 끝났습니다. 502 Bad Gateway와의 차이는 백엔드에 연결할 수는 있지만 너무 느리다는 점입니다. 일반적인 타이머는 nginx에서 60초(fastcgi_read_timeout, proxy_read_timeout), Apache에서 60초(ProxyTimeout), Cloudflare에서 100초이며, Cloudflare의 값은 무료 요금제에서 변경할 수 없습니다.
시간 제한을 올리는 것은 좀처럼 해결책이 되지 않습니다. 무언가가 너무 오래 걸리고 있으며, 찾아야 할 것은 바로 그 무언가입니다.
흔한 원인
- 느린 데이터베이스 쿼리. 페이지를 불러올 때마다 거대한 테이블의 행 수를 세는 WordPress 플러그인, 누락된 인덱스, 만료된 트랜지언트로 가득 찬
wp_options테이블. 모든 요청이 기다리며, 부하가 걸리면 대기 시간이 쌓입니다. - 멈춰 있는 외부 호출. 애플리케이션이 결제 제공업체, 메일 서비스, 날씨 API, 라이선스 서버에 접속하는데 그 서비스가 느리거나 다운된 경우입니다. 해당 호출에 시간 제한이 없으면 페이지가 그 응답을 기다립니다.
- 너무 적은 워커. 모든 PHP-FPM 워커가 느린 요청 처리로 바쁘고, 새 요청은 앞단이 포기할 때까지 대기합니다. 외부에서 보면 개별 요청은 빠를 수 있어도 시간 초과처럼 보입니다.
- 포그라운드에서 실행되는 Cron 또는 백업. 백업 플러그인, 가져오기, URL 50,000개의 사이트맵을 웹 요청 안에서 생성하면 어떤 시간 제한도 초과합니다.
- 디스크 I/O 또는 스왑. 서버의 메모리가 부족해 스왑이 발생하면 모든 것이 열 배 느려집니다. CPU 사용률은 낮은데 부하가 높은 것으로 나타납니다.
- 앞단과 뒷단 사이의 네트워크. 한 데이터 센터에는 로드 밸런서, 다른 곳에는 백엔드가 있고 방화벽이 조용히 패킷을 버리는 경우입니다. 드문 경우이지만, 백엔드가 한가한데 모든 요청이 504라면 이것을 의심하세요.
방문자인 경우
1분 후에 새로 고침하세요. 사이트가 다운된 것이 아니라 느린 것이라면 다른 페이지를 시도해 보세요. 시간 초과가 무거운 특정 페이지 하나와 관련 있을 수 있습니다.
직접 운영하는 사이트라면: 먼저 확인할 것
웹사이트 다운 확인 도구를 실행하세요. 외부에서 본 상태 코드와 응답 시간을 보여 줍니다. 오늘 30초 만에 응답하는 사이트는 내일 부하가 걸리면 504를 표시할 사이트입니다.
직접 운영하는 서버에서 시간이 어디에서 소모되는지 찾아보세요.
# 워커가 쌓이고 있는가?
sudo tail -n 30 /var/log/nginx/error.log | grep -E 'timed out|timeout'
# PHP가 지금 무엇을 하고 있는가? (풀에 pm.status_path 필요)
curl -s 'http://127.0.0.1/fpm-status?full' | grep -E 'state|request URI|duration' | head -40
# MySQL/MariaDB: 어떤 쿼리가 얼마 동안 실행 중인가?
sudo mysql -e 'SHOW FULL PROCESSLIST;'
# 메모리와 스왑
free -m
vmstat 1 5
같은 쿼리로 가득 찬 프로세스 목록이나 0이 아닌 vmstat의 si/so 열이 원인을 알려 줍니다.
직접 운영하는 서버에서의 해결 방법
1. 느린 쿼리 로그를 켜고 쿼리 수정
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 2;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
몇 분 후 sudo mysqldumpslow -s t /var/log/mysql/slow.log | head가 최악의 쿼리를 보여 줍니다. 누락된 인덱스를 추가하거나, 해당 쿼리를 실행하는 플러그인을 찾아 교체하세요. WordPress에서는 비대해진 options 테이블이 대표적인 원인입니다.
wp transient delete --all
wp db query "SELECT option_name, LENGTH(option_value) AS size FROM wp_options WHERE autoload='yes' ORDER BY size DESC LIMIT 20;"
매 요청마다 자동으로 불러오는 항목 중 수백 킬로바이트에 이르는 것은 삭제 후보입니다.
2. 외부 호출에 시간 제한 설정
애플리케이션이 보내는 모든 HTTP 호출에는 웹 서버보다 짧은 자체 시간 제한이 필요합니다. cURL을 사용하는 PHP에서는 CURLOPT_CONNECTTIMEOUT 5, CURLOPT_TIMEOUT 10입니다. WordPress의 wp_remote_get()은 timeout 인자를 받는데, 많은 플러그인이 기본값 그대로 두어 끝없이 기다립니다. 타사 서비스가 다운되면 페이지가 죽지 않고 기능이 줄어든 채로 표시되어야 합니다(실시간 배송비 없이 양식을 표시하는 식으로).
3. 오래 걸리는 작업을 요청 밖으로 이동
백업, 가져오기, 내보내기, 큰 사이트맵, 메일 발송은 페이지 조회가 아니라 Cron 작업이나 큐에서 처리해야 합니다. WordPress에서는 의사(pseudo) cron을 끄고 진짜 cron을 실행하세요.
// wp-config.php
define('DISABLE_WP_CRON', true);
# crontab -e
*/5 * * * * cd /var/www/example.com && wp cron event run --due-now >/dev/null 2>&1
4. 시간 제한을 신중하게 조정
특정 페이지 하나가 정당하게 더 오래 걸려야 한다면(관리자 내보내기, 보고서) 전체가 아니라 해당 위치에만 시간 제한을 올리세요.
location = /wp-admin/export.php {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 300;
}
PHP-FPM을 쓰는 Apache: <Location> 블록 안에 ProxyTimeout 300. PHP 자체에도 max_execution_time이 있으니 같은 값으로 설정하세요. 그렇지 않으면 PHP가 먼저 중지되어 504 대신 500이 표시됩니다. 그런 다음 sudo nginx -t && sudo systemctl reload nginx를 실행하세요.
5. 스왑 멈추기
ps aux --sort=-%mem | head
PHP-FPM 워커가 각각 150 MB를 사용하는데 4 GB 서버에서 30개를 실행한다면 계산이 맞지 않습니다. pm.max_children을 낮추거나, 메모리를 늘리거나, 메모리를 많이 쓰는 플러그인을 고치세요. 스왑하지 않는 서버는 504가 대부분 사라지는 서버입니다.
6. 캐시
전체 페이지 캐시(nginx FastCGI 캐시, WordPress 캐시 플러그인, Varnish)는 PHP나 데이터베이스를 건드리지 않고 재방문자에게 응답합니다. 홈페이지와 상위 페이지가 캐시에서 제공되기 시작하면 부하 시의 504는 대부분 사라집니다.
호스팅 업체의 문제인 경우
공유 호스팅에서는 느린 쿼리와 플러그인은 고칠 수 있지만 서버의 시간 제한이나 이웃 계정의 부하는 고칠 수 없습니다. 호스팅 업체에 다음 내용을 보내세요.
안녕하세요. 제 웹사이트 example.com이 약 16:00 UTC부터 “504 Gateway Timeout”을 반환합니다. 예전에는 2초 이내에 로드되던 페이지가 이제 시간 초과됩니다. 아무것도 변경하지 않았습니다. 제 계정의 데이터베이스 서버나 PHP 풀이 과부하 상태인지, 또는 서버의 다른 계정이 높은 부하를 일으키고 있는지 확인하시고 결과를 알려 주시기 바랍니다.
다음번을 막으려면
504는 느린 사이트가 더 느려진 것이며, 응답 시간은 시간 초과가 나기 훨씬 전부터 측정할 수 있습니다. 웹사이트 확인은 모든 확인의 응답 시간을 기록합니다. 이에 대한 알림을 설정하면 방문자가 오류를 보기 몇 주 전에 추세를 확인할 수 있습니다. 서버 에이전트는 서버 내부의 부하, 메모리, 스왑을 추가로 보여 줍니다. 직접 운영하는 서버에서는 Deep Fix가 느린 쿼리나 멈춰 있는 호출을 찾아 해결하며, 15분 단위로 청구되고 주문 전에 가격을 확인할 수 있습니다.
다음 단계
다운되었나요? 당사가 대응합니다.
직접 운영하는 서버나 SSH 접근이 가능한 웹 공간에서: 흔한 원인은 고정 가격의 Quick Fix로, 나머지는 15분 단위로 청구되는 Deep Fix로 24시간 처리합니다. 주문 전에 가격을 확인할 수 있습니다.
복구 요청하기