2편에서 LEMP를 올리고 phpinfo까지 확인했습니다. 그다음 워드프레스 파일을 풀고, wp-config.php에 DB 정보와 보안 키를 채워 넣고, 설레는 마음으로 브라우저를 켰습니다. 그리고 이 화면을 봤습니다.

“페이지가 작동하지 않습니다. HTTP ERROR 500.” 끝입니다. 힌트가 없어요. 이럴 때 대부분은 구글에 “워드프레스 500 에러”를 칩니다. 저도 처음엔 그랬고, 그게 제일 오래 걸리는 길이었습니다. 답은 이미 제 서버 안에 텍스트로 적혀 있었습니다. 로그 파일 한 줄을 읽는 데 5분, 고치는 데 1분이었습니다. 이번 편은 그 5분을 어떻게 쓰는지에 대한 이야기입니다.
500은 사실 꽤 친절한 오류다
먼저 오해부터 풉시다. 500은 “원인 불명”이 아닙니다. 서버가 요청을 처리하다가 내부에서 터졌다는 아주 구체적인 신고입니다. 그리고 터진 쪽은 거의 항상 PHP입니다.
브라우저에 아무 설명이 없는 건 서버가 몰라서가 아니라, 일부러 안 알려주는 것입니다. 운영 환경의 PHP는 display_errors = Off가 기본입니다. 오류 내용을 화면에 뿌리면 파일 경로·DB 이름·코드 구조가 통째로 노출되니까요. 그래서 화면은 비우고, 내용은 로그에만 적습니다. 즉 500 화면은 “서버 안에 답을 적어뒀으니 와서 읽어라”는 뜻입니다.
2편에서 502를 다뤘는데, 이 둘을 구분하는 게 첫 갈림길입니다.
| 코드 | 무슨 뜻인가 | 어디를 의심하나 |
|---|---|---|
| 4xx (404·403) | 요청 쪽 문제 | 주소·권한·서버 블록 설정 |
| 500 | PHP는 실행됐는데 스크립트가 터짐 | PHP 코드·문법·확장·DB 연결 |
| 502 | Nginx가 PHP-FPM에 말을 못 걸음 | 소켓 경로, PHP-FPM 프로세스 상태 |
| 503 | 서버가 감당 못 함 | 과부하, 서비스 중지 |
| 504 | PHP가 응답을 안 줌 | 타임아웃, 무한 루프 |
이 표가 왜 중요하냐면, 500이 떴다는 건 이미 Nginx ↔ PHP-FPM 연결이 정상이라는 증거이기 때문입니다. 2편에서 고생한 소켓 경로는 범인이 아닙니다. 범위가 확 좁혀졌습니다.
참고 — 500과 “백지 화면(WSOD)”은 사실상 형제입니다. PHP가 치명적 오류로 죽었는데 본문을 아무것도 못 내보낸 상황이라, 크롬은 500 안내 페이지를 대신 그려줍니다. 원인 찾는 방법은 똑같습니다.
추측을 멈추는 게 1단계다
500을 만나면 손이 먼저 움직입니다. 플러그인을 지워볼까, 테마를 바꿔볼까, 퍼미션을 777로 줘볼까. 전부 하지 마세요. 원인을 모르는 채로 바꾸면 원인이 하나 더 늘어날 뿐입니다. 특히 777은 문제를 안 고치면서 보안 구멍만 냅니다.
순서는 하나입니다. 로그를 먼저 본다. 2편에서 외워두라고 했던 경로 세 개입니다.
| 로그 | 경로 | 500일 때 여기에 뭐가 찍히나 |
|---|---|---|
| Nginx 오류 | /var/log/nginx/error.log | PHP의 치명적 오류가 그대로 전달됨 — 여기부터 |
| PHP-FPM | /var/log/php8.3-fpm.log | 워커 프로세스가 죽거나 재시작될 때 |
| 워드프레스 디버그 | /var/www/ddlabx/wp-content/debug.log | WP_DEBUG_LOG를 켰을 때만 생성 |
실시간으로 보는 방법이 제일 확실합니다. 터미널 창을 하나 더 열어서 로그를 띄워두고, 다른 창(브라우저)에서 새로고침하는 겁니다.
sudo tail -f /var/log/nginx/error.log
이 상태에서 브라우저 새로고침을 누르면, 바로 그 순간 화면에 로그가 흘러나옵니다. 방금 내가 만든 오류라는 게 100% 확실해집니다. 로그를 보다가 “이게 아까 것인지 지금 것인지” 헷갈릴 일이 없어요. Ctrl+C로 빠져나옵니다.
이미 지나간 오류를 볼 거면 마지막 부분만:
sudo tail -50 /var/log/nginx/error.log
로그 한 줄 해부하기
제 화면에 뜬 줄은 대략 이런 모양이었습니다. (실제 로그는 한 줄로 길게 이어집니다)
2026/07/12 21:14:07 [error] 1043#1043: *7 FastCGI sent in stderr:
"PHP message: PHP Parse error: syntax error, unexpected token "'",
expecting "," or ";" in /var/www/ddlabx/wp-config.php on line 52"
while reading response header from upstream, client: 121.xxx.xxx.xxx,
server: _, request: "GET / HTTP/1.1",
upstream: "fastcgi://unix:/run/php/php8.3-fpm.sock:", host: "141.164.37.195"
길어 보이지만 읽을 곳은 정해져 있습니다. 조각별로 자릅니다.
| 조각 | 의미 | 중요도 |
|---|---|---|
[error] | 심각도. warn이면 급하지 않음 | 낮음 |
FastCGI sent in stderr | PHP가 뱉은 말을 Nginx가 옮겨적음 | 중간 — 범인이 PHP라는 확증 |
PHP Parse error: syntax error | 문법 오류. 코드가 아예 안 읽힘 | 최고 |
in /var/www/ddlabx/wp-config.php | 어느 파일인지 | 최고 |
on line 52 | 몇 번째 줄인지 | 최고 |
upstream: fastcgi://...sock | PHP-FPM과 정상 통신 중 | 낮음 — 502가 아님을 재확인 |
host: 141.164.37.195 | IP로 접속했음 | 낮음 |
즉 이 한 줄이 “파일명 + 줄 번호 + 오류 종류”를 다 알려줬습니다. 구글링이 필요 없었어요. 답이 처음부터 여기 있었습니다.
참고 — Parse error와 Fatal error는 다릅니다. Parse error(문법 오류)는 PHP가 파일을 읽지도 못한 겁니다. 코드가 단 한 줄도 실행되지 않았어요. 반면 Fatal error는 실행은 됐는데 도중에 죽은 겁니다. 지금은 전자입니다.
범인은 wp-config.php — 그리고 nano 붙여넣기
52번째 줄을 열어봤습니다. 워드프레스 보안 키(salt) 구역이었습니다.
워드프레스를 수동 설치하면 wp-config.php에 여덟 줄짜리 인증 키를 넣는 단계가 있습니다. 공식 생성기에서 무작위로 만들어주는 그 긴 문자열이요. 저는 그걸 복사해서 SSH 창의 nano에 붙여넣었습니다. 그리고 사이트가 죽었습니다.
붙여넣기 한 번에 왜 문법이 깨지느냐 — 터미널에 대량의 텍스트를 밀어넣는 건 생각보다 안 미덥습니다.
| 깨지는 방식 | 왜 생기나 | 결과 |
|---|---|---|
| 문자 누락 | 긴 텍스트가 빠르게 들어올 때 터미널·SSH 구간에서 유실 | 따옴표 한 짝이 사라짐 → Parse error |
| 자동 들여쓰기 누적 | 편집기 autoindent가 줄마다 공백을 덧붙임 | 줄이 밀리며 구조 붕괴 |
| 줄 어긋남 | 긴 줄이 화면에서 접혀 보여 눈으로 검증 불가 | 깨진 걸 못 보고 저장 |
| 기존 줄 미삭제 | placeholder를 안 지우고 아래에 붙임 | 키 중복 정의 |
제 경우는 따옴표 짝이 안 맞았습니다. PHP 입장에선 define('AUTH_KEY', 'abc 에서 문자열이 안 닫힌 채 다음 줄로 넘어가니, unexpected token "'"라고 비명을 지른 겁니다.
주의 — 이 상황에서 WP_DEBUG를 켜는 건 소용없습니다. 디버그 설정도 wp-config.php 안에 있는데, 그 파일 자체가 안 읽히니까 설정이 적용될 리가 없죠. 문법 오류일 때 디버그 플래그는 무용지물입니다. 로그만이 답입니다.
고치는 법 — php -l 한 줄이면 끝난다
PHP에는 문법만 검사하는 명령이 내장돼 있습니다. 실행은 안 하고 문법만 봅니다. 이걸 몰라서 헤매는 분이 많아요.
php -l /var/www/ddlabx/wp-config.php
깨져 있으면 이렇게 나옵니다.
PHP Parse error: syntax error, unexpected token "'" in /var/www/ddlabx/wp-config.php on line 52
Errors parsing /var/www/ddlabx/wp-config.php
정상이면 딱 한 줄입니다.
No syntax errors detected in /var/www/ddlabx/wp-config.php
이제 해당 줄로 바로 갑니다. nano는 +줄번호로 그 줄에 착지할 수 있습니다.
sudo cp /var/www/ddlabx/wp-config.php ~/wp-config.php.bak
sudo nano +52 /var/www/ddlabx/wp-config.php
고쳤으면 저장하고 다시 검사합니다. 브라우저로 확인하지 마세요. php -l이 훨씬 빠르고 정확합니다.
php -l /var/www/ddlabx/wp-config.php
No syntax errors detected가 뜨면 그때 새로고침합니다. 저는 여기서 설치 화면이 떴습니다.
wp-config.php에서 자주 나는 문법 오류를 정리하면:
| 로그 메시지 | 원인 | 고치는 곳 |
|---|---|---|
unexpected token "'" | 따옴표 짝이 안 맞음 | 해당 줄의 ' 개수를 세기 |
unexpected end of file | 괄호나 세미콜론이 안 닫힘 | 보통 그 위쪽 줄 |
unexpected token ";" | define(...) 괄호가 덜 닫힘 | 괄호 짝 확인 |
Constant AUTH_KEY already defined | 기존 키를 안 지우고 새로 추가 | 중복된 define 줄 삭제 |
headers already sent | 파일 맨 앞 <?php 앞이나 맨 끝에 공백·빈 줄 | 파일 끝의 ?>는 아예 지우는 게 안전 |
다시 안 겪는 법
같은 일을 두 번 겪지 않으려고 습관을 세 개 바꿨습니다.
1. 긴 내용은 붙여넣지 말고 scp로 올린다. 로컬에서 파일을 완성해서 통째로 전송하면 터미널을 안 거치니 깨질 구간이 없습니다.
scp wp-config.php mj@서버IP:/tmp/
sudo mv /tmp/wp-config.php /var/www/ddlabx/
sudo chown www-data:www-data /var/www/ddlabx/wp-config.php
2. PHP 파일을 건드렸으면 저장 직후 php -l. 2초입니다. 브라우저로 확인하는 것보다 빠릅니다.
3. 고치기 전에 백업. cp 파일 파일.bak 한 줄이면 되돌릴 길이 생깁니다. 이게 없으면 고치다 더 망가뜨렸을 때 시작점이 사라집니다.
참고 — 보안 키를 다시 발급받아도 아무 문제 없습니다. 기존 로그인 세션만 전부 풀려서 다시 로그인하면 됩니다. 깨진 키를 살리려고 애쓰지 말고 새로 붙이는 게 빠릅니다.
500이 wp-config 때문이 아닐 때
이번 건은 문법 오류였지만, 500은 다른 이유로도 뜹니다. 로그에 Parse error가 아니라 다른 게 찍혔다면 아래를 보세요.
| 로그에 찍히는 말 | 진짜 원인 | 대응 |
|---|---|---|
Call to undefined function ... | PHP 확장 누락 (2편 표 참고) | sudo apt install php8.3-확장명 |
Allowed memory size exhausted | PHP 메모리 한도 초과 | wp-config에 define('WP_MEMORY_LIMIT','256M'); |
Error establishing a database connection | DB 정보 오타 / MySQL 중지 | systemctl status mysql |
Permission denied | 소유권이 www-data가 아님 | sudo chown -R www-data:www-data /var/www/ddlabx |
| 특정 플러그인 경로가 찍힘 | 플러그인 충돌 | 해당 플러그인 폴더명을 바꿔 비활성화 |
| 로그에 아무것도 안 찍힘 | Nginx 서버 블록 문제 | sudo nginx -t |
공통점이 보이시나요. 전부 로그가 먼저 말해줍니다. 대응이 다를 뿐 진입로는 하나입니다.
자주 묻는 질문
Q. 500 뜨자마자 재부팅하면 안 되나요?
A. 대개 소용없고, 오히려 손해입니다. 문법 오류는 재부팅해도 파일이 그대로라 똑같이 납니다. 더 나쁜 건 재부팅으로 메모리에 있던 단서가 날아간다는 점입니다. 로그부터 보세요.
Q. 워드프레스 관리자에 못 들어가는데 플러그인은 어떻게 끄나요?
A. 파일 시스템에서 끕니다. /wp-content/plugins/의 폴더 이름을 바꾸면(예: 플러그인명_off) 워드프레스가 못 찾아서 자동으로 비활성화됩니다. 전체를 한 번에 끄려면 plugins 폴더 자체의 이름을 바꾸면 됩니다. 되살릴 땐 이름만 원래대로.
Q. WP_DEBUG는 언제 켜나요?
A. wp-config.php가 정상적으로 읽히는데도 사이트가 이상할 때입니다. 플러그인·테마 오류 추적엔 유용합니다. 다만 WP_DEBUG_DISPLAY는 반드시 false로 두고 WP_DEBUG_LOG만 켜세요. 화면에 오류를 뿌리면 방문자에게 서버 경로가 보입니다. 그리고 다 잡았으면 끄세요.
Q. 로그가 너무 길어서 뭘 봐야 할지 모르겠어요.
A. tail -f로 띄워두고 새로고침하세요. 그 순간 새로 생기는 줄만 보면 됩니다. 그래도 많으면 sudo grep -i "PHP" /var/log/nginx/error.log | tail -20으로 PHP 관련만 거르세요.
Q. 처음부터 원클릭 설치를 썼으면 안 겪었을 일 아닌가요?
A. 맞습니다. 대신 이번에 로그 읽는 법을 배웠습니다. 이후로 사이트에 문제가 생기면 tail -f부터 칩니다. 5분이면 원인이 나와요. 원클릭으로 시작했다면 이 500 화면 앞에서 지금도 구글링하고 있었을 겁니다.
정리하면
- 500 = PHP는 돌았는데 스크립트가 터짐. 502(연결 실패)와 다릅니다. 500이 떴다는 건 Nginx-PHP 연결은 멀쩡하다는 뜻입니다.
- 화면이 비어 있는 건 서버가 몰라서가 아니라 보안상 로그에만 적기 때문입니다.
- 순서는 하나 — 추측 금지,
sudo tail -f /var/log/nginx/error.log먼저. - 로그 한 줄에 파일명 + 줄 번호 + 오류 종류가 다 있습니다. 거기만 읽으면 됩니다.
- PHP 파일을 고쳤으면
php -l 파일명. 브라우저 새로고침보다 빠르고 정확합니다. - 문법 오류일 때 WP_DEBUG는 무용지물입니다. 그 설정도 안 읽히는 파일 안에 있으니까요.
- 긴 텍스트는 nano에 붙여넣지 말고 scp로 올리세요. 고치기 전엔
.bak백업.
이제 사이트가 IP로 뜹니다. 다음 편은 서버의 보안을 위해 바로 적용해야 하는 5가지에 적용에 대한 내용입니다.