워드프레스·호스팅

wp-config.php 필수 설정 12가지, 보안과 성능 한 번에 점검하기

wp-config.php 필수 설정 12가지, 보안과 성능 한 번에 점검하기

워드프레스를 설치하면 관리자 화면에서 거의 모든 걸 만질 수 있습니다. 사이트 제목, 퍼머링크, 테마, 플러그인까지 클릭으로 끝납니다. 그런데 어느 순간 “메모리 부족” 에러가 뜨거나, 도메인을 바꾸고 나서 관리자 로그인이 안 되거나, 원인 모를 백지 화면이 나오면 관리자 화면에서는 손쓸 방법이 없습니다. 그때 열어야 하는 게 wp-config.php입니다.

이 파일은 워드프레스가 가장 먼저 읽는 설정 원본입니다. DB 접속 정보부터 로그인 쿠키 암호화 키, 디버그 스위치, 메모리 한계까지 한 파일에 몰려 있습니다. 요즘 “보안 플러그인 깔았는데도 무겁다”, “캐시 플러그인이 이상한 줄을 넣었다” 같은 상황이 흔한데, 그 대부분이 실제로는 이 파일 한두 줄로 정리됩니다. 반대로 이 파일이 한 글자라도 깨지면 사이트 전체가 백지가 됩니다. 그래서 손대기 전에 백업이 먼저입니다.

한눈에 · 파일 위치 확인 → 백업 복사 → DB 4줄 점검 → SALT 교체·권한 조정 → 디버그 로그 설정 → 성능·URL 설정 → php -l 문법 검사 → 사이트 접속 테스트

wp-config.php는 워드프레스의 설정 원본

워드프레스 설치 과정에서 DB 정보를 입력하면, 워드프레스는 wp-config-sample.php라는 템플릿을 복사해 값만 채워 wp-config.php를 만듭니다. 즉 이 파일은 자동 생성물이지만, 그 뒤로는 사람이 직접 관리해야 하는 파일입니다. 샘플 파일은 지우지 말고 그냥 두는 편이 낫습니다. 나중에 원래 구조를 참고할 일이 생깁니다.

이 파일에 들어가는 값은 대부분 define() 형태의 PHP 상수입니다. 상수는 한 번 정해지면 실행 중에 바꿀 수 없는 값입니다. 그래서 워드프레스는 “관리자 화면에서 바꾸면 위험하거나, 바꿀 수 없어야 하는 것”들을 여기에 몰아뒀습니다. 관리자 화면에 없는 설정은 거의 다 이 파일에 있다고 보면 됩니다.

이 파일이 로드되는 시점

방문자가 페이지를 열면 index.php → wp-blog-header.php → wp-load.php → wp-config.php 순서로 실행됩니다. 플러그인도, 테마도, DB 연결도 그다음입니다. 워드프레스 코어가 뜨기 전에 읽히는 파일이라는 뜻입니다. 그래서 여기서 정한 값은 플러그인이 나중에 뭘 하든 기준점이 됩니다.

관리자 화면 설정과 우선순위 관계

같은 항목이 관리자 화면과 이 파일에 둘 다 있으면, 보통 wp-config.php가 이깁니다. 대표적인 게 사이트 주소입니다. WP_HOME, WP_SITEURL을 파일에 적어두면 설정 > 일반 화면의 주소 입력칸이 회색으로 비활성화됩니다. DB에 저장된 값보다 파일 값을 먼저 쓰기 때문입니다. 이 성질을 알면 나중에 도메인 사고가 났을 때 탈출구가 됩니다.

파일 위치 찾기와 안전하게 여는 순서

중요성은 봤으니 이제 내 서버에서 어디 있는지부터 찾아야 합니다. 위치는 호스팅사와 설치 방식에 따라 다릅니다. 흔한 경로는 /home/계정명/public_html/wp-config.php, /var/www/html/wp-config.php, /usr/share/nginx/html/wp-config.php 정도입니다. cPanel 계열이면 public_html, 직접 세팅한 우분투 서버면 /var/www 아래일 가능성이 높습니다.

SSH에서 찾기

경로를 모를 때는 그냥 찾는 게 빠릅니다.

find / -name wp-config.php -type f 2>/dev/null

2>/dev/null은 권한 없는 디렉터리에서 나오는 Permission denied 메시지를 버리는 부분입니다. 이게 없으면 화면이 에러로 도배됩니다. 결과가 두 개 이상 나오면 사이트가 여러 개거나, 백업 폴더에 옛 파일이 남아 있는 경우입니다. 경로를 보고 실제 웹루트를 골라야 합니다. find 옵션을 더 알고 싶으면 find로 파일 찾아 한 번에 처리하기, -exec와 xargs 실전 정리를 참고하세요.

참고로 wp-config.php는 웹루트 한 단계 위에 있어도 워드프레스가 알아서 찾습니다. wp-load.php가 상위 디렉터리까지 확인하기 때문입니다. 단, 그 상위 디렉터리에 다른 워드프레스가 없어야 합니다. 서브디렉터리 멀티 설치 환경에서는 엉뚱한 파일을 물 수 있어서, 이 구조는 설치 형태에 따라 다릅니다.

백업 만드는 한 줄

찾았으면 바로 열지 말고 복사부터 합니다.

cd /home/계정명/public_html
cp wp-config.php wp-config.php.bak.$(date +%Y%m%d)
ls -l wp-config.php*

날짜를 붙이는 이유는 단순합니다. 두 번, 세 번 수정하다 보면 .bak 하나로는 언제 상태인지 알 수 없게 됩니다. ls -l로 두 파일이 같은 크기로 보이면 복사가 제대로 된 겁니다.

한 가지 주의점. 백업 파일을 웹루트에 wp-config.php.txt나 wp-config.bak 같은 이름으로 두면 브라우저에서 그대로 열릴 수 있습니다. PHP로 실행되지 않는 확장자면 소스가 노출됩니다. DB 비밀번호가 그대로 보입니다. 백업은 웹루트 밖으로 옮기거나, 작업이 끝나면 지우는 게 안전합니다.

편집 후 문법 확인: php -l wp-config.php

편집은 SSH에서 vi나 nano로 하는 게 가장 안전합니다. FTP 편집기는 저장 중 연결이 끊기면 파일이 0바이트로 남는 사고가 있습니다. 그리고 윈도우 메모장으로 열어 저장하면 BOM(파일 맨 앞에 붙는 눈에 안 보이는 3바이트)이 들어가서 헤더 에러가 납니다. vi 기본기가 부족하면 vi 편집기, 딱 이 10개 명령만 알면 서버에서 안 막힙니다를 먼저 보고 오시면 편합니다.

저장했으면 사이트를 열기 전에 문법을 검사합니다.

php -l wp-config.php

No syntax errors detected in wp-config.php가 나오면 통과입니다. 세미콜론을 빼먹었다면 이렇게 나옵니다.

PHP Parse error: syntax error, unexpected 'define' (T_STRING) in wp-config.php on line 42

줄 번호가 찍히니 바로 고칠 수 있습니다. 이 한 줄을 습관으로 만들면 백지 화면을 절반은 피할 수 있습니다.

DB 접속 정보와 테이블 접두사, 제일 먼저 볼 네 줄

백업과 문법 검사 방법을 확보했으니 이제 파일 위에서부터 읽어 내려갑니다. 맨 처음 나오는 게 DB 접속 정보 네 줄입니다.

define( 'DB_NAME', 'wp_mysite' );
define( 'DB_USER', 'wp_user' );
define( 'DB_PASSWORD', '••••••••' );
define( 'DB_HOST', 'localhost' );

각각 데이터베이스 이름, 접속 계정, 비밀번호, DB 서버 주소입니다. 화면에 Error establishing a database connection이 떴다면 십중팔구 이 네 줄 아니면 DB 서버 자체 문제입니다. 비밀번호에 특수문자가 있을 때 따옴표 종류를 잘못 써서 깨지는 경우도 흔합니다. 작은따옴표 안에 작은따옴표가 들어가면 문법이 깨집니다. 이럴 때는 큰따옴표로 감싸거나 백슬래시로 이스케이프합니다.

DB_HOST가 다른 경우

localhost가 기본값이지만 항상 그렇지는 않습니다. 공유 호스팅에서는 DB 서버가 물리적으로 다른 장비에 있어서 mysql.호스팅사.com처럼 별도 호스트명을 주는 경우가 있습니다. 포트가 다르면 db.example.com:3307처럼 콜론으로 붙입니다. 도커로 올린 환경이면 컨테이너 이름(db, mysql)이 그대로 호스트가 됩니다.

호스트명이 맞는지 확인하려면 명령줄에서 직접 붙어보는 게 확실합니다.

mysql -h localhost -u wp_user -p wp_mysite

ERROR 1045 (28000): Access denied for user가 나오면 계정이나 비밀번호 문제입니다. ERROR 2002 (HY000): Can't connect to local MySQL server면 DB 서비스가 죽어 있는 상태입니다. 이건 wp-config.php를 고칠 문제가 아니라 서비스를 살릴 문제입니다.

접두사 변경이 위험한 이유

그 아래에는 테이블 접두사가 있습니다.

$table_prefix = 'wp_';

워드프레스가 만드는 테이블 이름 앞에 붙는 문자열입니다. wp_posts, wp_options 같은 식입니다. DB 하나에 워드프레스 여러 개를 설치할 때 구분하는 용도로 쓰입니다.

“보안상 접두사를 바꾸라”는 조언을 자주 보는데, 이미 설치가 끝난 사이트에서 이 줄만 바꾸면 사이트가 즉시 망가집니다. 워드프레스는 새 접두사로 테이블을 찾다가 없으니 재설치 화면을 띄웁니다. 진짜로 바꾸려면 DB 테이블 이름 전체를 RENAME TABLE로 바꾸고, wp_options의 wp_user_roles 행과 wp_usermeta의 wp_capabilities, wp_user_level 키까지 손봐야 합니다. 위험 대비 효과가 크지 않아서, 운영 중이라면 그냥 두는 판단도 충분히 합리적입니다.

문자셋 설정

define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );

utf8mb4는 한 글자를 최대 4바이트로 저장하는 방식입니다. MySQL의 옛 utf8은 3바이트까지만 지원해서 이모지나 일부 한자·특수문자를 저장하면 잘려나갑니다. 글 저장했더니 이모지 뒤 내용이 사라지는 증상이 이것입니다. 새로 설치한 사이트라면 대개 utf8mb4로 되어 있습니다. 오래된 사이트를 이관했다면 이 값과 실제 테이블 문자셋이 다를 수 있으니 확인해 볼 만합니다.

보안 키(SALT)와 접속 관련 설정 손보기

DB 네 줄이 “서버에 붙는 열쇠”라면, 그 아래 구간은 “로그인 세션을 지키는 열쇠”입니다. AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY와 각각의 _SALT 짝, 총 8개 값이 들어 있습니다.

이 값들은 로그인 쿠키를 암호화하고 서명하는 데 쓰입니다. 값이 길고 무작위일수록 쿠키를 위조하기 어려워집니다. 문제는 수동 설치나 잘못된 이관 과정에서 put your unique phrase here라는 기본 문구가 그대로 남는 경우입니다. 이러면 쿠키 위조 방어가 사실상 없는 상태가 됩니다.

SALT 새로 발급받기

워드프레스가 무료 생성기를 제공합니다.

https://api.wordpress.org/secret-key/1.1/salt/

브라우저로 열면 8줄짜리 define() 블록이 바로 나옵니다. 새로고침할 때마다 값이 바뀝니다. 서버에서 바로 받아보려면 이렇게 합니다.

curl -s https://api.wordpress.org/secret-key/1.1/salt/

받은 8줄을 기존 8줄과 통째로 바꿔치기하면 끝입니다. 한 줄만 바꾸는 게 아니라 블록 전체를 교체하는 게 맞습니다.

부작용이 하나 있습니다. 키를 교체하면 로그인 중인 모든 사용자가 즉시 로그아웃됩니다. 기존 쿠키가 새 키로는 검증되지 않으니까요. 게시글이나 회원 데이터가 사라지는 건 아니고 세션만 끊깁니다. 그래도 회원제 사이트나 쇼핑몰이라면 트래픽 적은 시간에 하는 게 낫습니다. 관리자 본인도 다시 로그인해야 하니, 비밀번호를 모르는 상태에서 하면 곤란해집니다.

SALT 교체 절차
SALT 교체 절차

파일 편집기 비활성화

워드프레스는 관리자 화면에서 테마와 플러그인의 PHP 파일을 직접 편집할 수 있게 해 줍니다. 편하지만 위험합니다. 관리자 계정이 한 번 털리면 공격자가 브라우저만으로 서버에 코드를 심을 수 있습니다.

define( 'DISALLOW_FILE_EDIT', true );

이 한 줄이면 외모양 > 테마 파일 편집기, 플러그인 > 플러그인 파일 편집기 메뉴가 사라집니다. 파일 수정은 SSH나 FTP로만 하게 되죠. 실무에서 코드 편집을 관리자 화면으로 하는 일은 거의 없으니 켜두는 편이 이득입니다.

여기에 관리자 페이지를 HTTPS로 강제하는 설정을 붙일 수 있습니다.

define( 'FORCE_SSL_ADMIN', true );

로그인 정보가 평문으로 흐르는 걸 막습니다. 다만 리버스 프록시나 로드밸런서 뒤에 있는 서버라면 이 설정만으로 리다이렉트 무한 루프가 생길 수 있습니다. 그럴 때는 아래 줄을 함께 넣습니다.

if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) {
    $_SERVER['HTTPS'] = 'on';
}

파일 권한 값 선택

파일 안의 설정만 잠그고 권한을 방치하면 반쪽입니다. wp-config.php에는 DB 비밀번호가 평문으로 들어 있으니, 같은 서버의 다른 계정이 읽을 수 없어야 합니다.

chmod 640 wp-config.php
ls -l wp-config.php

-rw-r-----로 바뀌면 성공입니다. 소유자는 읽고 쓰고, 같은 그룹은 읽기만, 나머지는 접근 불가입니다.

권한표기적합한 환경주의점
644-rw-r--r--기본값. 아무나 읽기 가능공유 호스팅에서 타 계정 노출 위험
640-rw-r-----Apache/PHP-FPM이 소유자 그룹에 속한 경우그룹 설정이 맞아야 동작
600-rw-------PHP가 파일 소유자와 같은 계정으로 실행될 때웹서버 유저가 다르면 500 에러

600으로 바꿨는데 사이트가 500 에러로 죽는다면, PHP를 실행하는 유저가 파일 소유자와 다른 경우입니다. 이때는 640으로 되돌리고 그룹을 확인하세요. 권한 숫자의 의미가 헷갈리면 chmod 755와 644의 실제 차이, 왜 폴더는 755여야 하나를 같이 보시면 정리가 됩니다.

디버그 설정, 화면에 에러를 뿌리지 않고 로그로 남기기

보안을 잠갔으면 이제 문제가 생겼을 때 원인을 잡을 장치를 만듭니다. 워드프레스의 디버그 스위치는 세 개가 한 세트로 움직입니다.

가장 흔한 실수는 WP_DEBUG만 true로 켜는 겁니다. 이러면 PHP 경고와 Notice가 사이트 화면 상단에 그대로 출력됩니다. 방문자에게 Warning: Undefined array key "x" in /home/user/public_html/wp-content/plugins/... 같은 문장이 보입니다. 서버 절대 경로까지 노출되니 보안상으로도 좋지 않습니다.

운영용 디버그 설정 3줄

운영 서버에서는 이 조합이 사실상 표준입니다.

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

의미를 나눠 보면 이렇습니다. 첫 줄은 에러 수집을 켭니다. 둘째 줄은 그걸 파일로 씁니다. 셋째 줄은 화면 출력을 끕니다. 네 번째 @ini_set은 PHP 자체의 출력 설정까지 눌러주는 보험입니다. WP_DEBUG_DISPLAY만으로 안 잡히는 경우가 있어서 함께 넣습니다.

로그 파일 위치를 바꾸고 싶으면 WP_DEBUG_LOG에 경로를 직접 줄 수도 있습니다.

define( 'WP_DEBUG_LOG', '/home/계정명/logs/wp-debug.log' );

웹으로 접근 안 되는 위치에 두는 게 더 안전합니다. 기본 위치인 wp-content/debug.log는 URL로 직접 열릴 수 있습니다.

로그 확인 명령

설정했으면 실제로 쌓이는지 봅니다.

tail -f wp-content/debug.log

-f는 파일이 커질 때 새 줄을 실시간으로 흘려보내는 옵션입니다. 이 상태로 두고 다른 창에서 문제가 나는 페이지를 새로고침하면, 그 순간 찍히는 에러를 볼 수 있습니다. 중단은 Ctrl + C입니다.

로그가 너무 많으면 원하는 줄만 뽑습니다.

grep -i "fatal error" wp-content/debug.log | tail -20

Fatal error는 실행이 멈춘 치명적 에러라서 백지 화면의 직접 원인일 가능성이 높습니다. Deprecated나 Notice는 급하지 않습니다. 필터링 옵션은 grep으로 로그에서 원하는 줄만 뽑기, 실전 옵션 정리에 정리해 뒀습니다.

디버그 로그 실시간 확인
디버그 로그 실시간 확인

다 쓰고 나면 반드시 끄기

개발용으로 두 개가 더 있습니다. SCRIPT_DEBUG는 압축된 JS/CSS 대신 원본을 로드해서 프론트엔드 디버깅에 쓰고, SAVEQUERIES는 실행된 모든 SQL을 메모리에 저장합니다. 둘 다 로컬 개발 환경에서만 켜야 합니다. 특히 SAVEQUERIES는 쿼리 하나하나를 배열에 담으니 메모리를 크게 먹습니다.

디버그를 켜둔 채 잊는 게 가장 흔한 사고입니다. Deprecated 경고를 뿜는 플러그인 하나가 있으면, debug.log가 며칠 만에 수 GB로 불어납니다. 디스크가 꽉 차면 이미지 업로드도, DB 쓰기도 실패합니다. 그런데 정작 에러 메시지는 “업로드 실패”라서 원인을 찾는 데 시간이 걸립니다. 용량이 갑자기 줄었을 때 범인을 찾는 순서는 디스크 용량 부족, df와 du로 범인 찾아내는 순서에 적어뒀습니다. 문제를 해결했으면 WP_DEBUG를 false로 되돌리고 로그 파일을 지우세요.

성능·용량에 직접 영향 주는 설정들

에러 잡는 장치를 마련했으면, 다음은 사이트가 저절로 무거워지는 원인들을 잘라냅니다. 이 구간은 눈에 잘 안 보이지만 몇 달 뒤 체감 차이가 큽니다.

리비전과 휴지통

워드프레스는 글을 수정할 때마다 이전 버전을 posts 테이블에 새 행으로 저장합니다. 기본값은 무제한입니다. 긴 글을 여러 번 고치면 리비전이 수십 개씩 쌓입니다. 글 100개짜리 사이트의 posts 테이블에 행이 2천 개 있는 상황이 실제로 생깁니다. 목록 조회와 검색이 느려지는 원인입니다.

define( 'WP_POST_REVISIONS', 5 );
define( 'AUTOSAVE_INTERVAL', 120 );
define( 'EMPTY_TRASH_DAYS', 7 );

첫 줄은 글마다 리비전을 최근 5개까지만 남깁니다. 완전히 끄려면 false를 주는데, 실수로 내용을 날렸을 때 되돌릴 방법이 없어져서 개인적으로는 3~5개 정도를 권합니다. 둘째 줄은 자동저장 주기를 초 단위로 늘립니다. 기본 60초가 잦다고 느껴질 때 씁니다. 셋째 줄은 휴지통에 들어간 글을 7일 뒤 자동 삭제합니다. 기본은 30일입니다.

주의할 점은 이 설정이 앞으로 생기는 데이터에만 적용된다는 것입니다. 이미 쌓인 리비전은 그대로 남습니다. 기존 것까지 정리하려면 DB에서 직접 지우거나 정리 플러그인을 한 번 돌려야 합니다.

메모리 한계

플러그인이 많아지면 Allowed memory size of 134217728 bytes exhausted 같은 에러를 만납니다. PHP가 쓸 수 있는 메모리를 다 썼다는 뜻입니다.

define( 'WP_MEMORY_LIMIT', '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );

첫 줄은 일반 페이지용, 둘째 줄은 관리자 화면과 이미지 처리용 한계입니다. 이미지 리사이징은 메모리를 많이 먹어서 별도로 잡습니다.

여기서 꼭 알아야 할 게 있습니다. 이 값은 서버의 php.ini 값을 넘어설 수 없습니다. php.ini가 128M인데 여기에 512M를 적어도 실제로는 128M입니다. 현재 값은 이렇게 확인합니다.

php -i | grep memory_limit

공유 호스팅이라면 php.ini를 직접 못 고치는 경우가 많습니다. 호스팅사 관리 패널에 PHP 설정 메뉴가 있는지 보고, 없으면 문의해야 합니다.

URL 하드코딩

도메인을 바꾸거나 HTTP에서 HTTPS로 옮기다가 관리자 로그인이 안 되는 상황, 겪어본 사람이 꽤 많습니다. DB의 siteurl 값이 옛 도메인을 가리켜서 계속 그쪽으로 리다이렉트되는 겁니다. 로그인 화면에 들어갈 수 없으니 관리자 화면에서 고칠 수도 없습니다.

이때 탈출구가 이 두 줄입니다.

define( 'WP_HOME', 'https://example.com' );
define( 'WP_SITEURL', 'https://example.com' );

파일 값이 DB 값보다 우선하니 즉시 새 도메인으로 접속됩니다. WP_HOME은 방문자가 보는 사이트 주소, WP_SITEURL은 워드프레스 코어 파일이 있는 주소입니다. 보통 같지만 코어를 서브디렉터리에 넣은 구조라면 다릅니다.

한 가지 트레이드오프가 있습니다. 이 줄이 있으면 설정 > 일반 화면의 주소 입력칸이 잠깁니다. 스테이징 서버에 파일을 그대로 복사했을 때 주소가 운영 도메인으로 고정되는 문제도 생깁니다. 급한 복구용으로 넣고, 정리가 끝나면 DB 값을 바로잡은 뒤 이 줄을 빼는 방식도 괜찮습니다.

자동 업데이트 정책

캐시 플러그인을 설치하면 파일 맨 위에 define('WP_CACHE', true);가 저절로 생깁니다. 플러그인이 넣은 줄이니 지우지 마세요. 지우면 캐시가 동작하지 않습니다. 반대로 플러그인을 삭제했는데 이 줄이 남아 있으면 지워도 됩니다.

코어 자동 업데이트는 값에 따라 동작이 크게 다릅니다.

값동작권장 상황
true메이저·마이너·보안 업데이트 모두 자동플러그인 적고 손 안 대는 개인 블로그
'minor'마이너·보안 업데이트만 자동 (기본 동작)대부분의 운영 사이트
false코어 자동 업데이트 전면 중단커스텀 개발이 들어간 사이트, 스테이징 검증 후 수동 반영
define( 'WP_AUTO_UPDATE_CORE', 'minor' );

'minor'는 문자열이니 따옴표가 필요하고, true와 false는 따옴표 없이 씁니다. 이걸 'true'로 적으면 문자열이 되어 의도와 다르게 동작할 수 있습니다. false로 막았다면 보안 패치를 직접 챙겨야 한다는 뜻이니, 방치하면 오히려 위험해집니다.

수정하다 막히는 지점과 복구 방법

여기까지 설정을 다 넣었는데 사이트가 안 열린다면, 이 섹션이 필요한 순간입니다. 실제로 사고가 나는 지점은 몇 개로 정해져 있습니다.

백지 화면 복구 순서

브라우저에 아무것도 안 나오는 White Screen of Death 상태라면 순서대로 갑니다.

  1. 백업으로 되돌립니다. cp wp-config.php.bak.20250612 wp-config.php
  2. 사이트가 살아나면 원인은 방금 넣은 줄입니다. 한 줄씩 다시 넣어가며 범인을 찾습니다.
  3. 백업으로도 안 살아나면 php -l wp-config.php로 문법을 봅니다.
  4. 문법이 멀쩡하면 웹서버 에러 로그를 봅니다. tail -50 /var/log/nginx/error.log 또는 tail -50 /var/log/apache2/error.log

백업 파일이 없으면 이 순서 전체가 무너집니다. 그래서 앞에서 백업을 먼저 다뤘습니다.

define 위치 규칙

파일 아래쪽에 이런 줄이 있습니다.

require_once ABSPATH . 'wp-settings.php';

이 줄이 워드프레스 코어를 실제로 부팅시키는 지점입니다. 모든 define()은 이 줄 위쪽에 있어야 합니다. 아래에 적으면 코어가 이미 값을 다 읽고 지나간 뒤라서 아무 효과가 없습니다. “설정을 넣었는데 아무 일도 안 일어난다”는 문의의 상당수가 이 위치 문제입니다.

관련해서 이런 경고도 봅니다.

PHP Notice: Constant WP_DEBUG already defined in wp-config.php on line 88

같은 상수를 두 번 정의했다는 뜻입니다. 붙여넣기를 반복하다 아래쪽에 중복이 생긴 경우가 많습니다. 먼저 정의된 값이 유효하고 나중 것은 무시됩니다. 파일에서 grep -n "WP_DEBUG" wp-config.php로 몇 줄에 있는지 찾아 하나만 남기세요.

에러 메시지별 대응

증상 / 메시지흔한 원인조치
Error establishing a database connectionDB 4줄 오타, DB 서비스 중단mysql -h ... -u ... -p로 직접 접속 테스트
Cannot modify header information - headers already sent파일 끝 ?> 뒤 공백, BOM마지막 ?> 삭제, BOM 제거
PHP Parse error: syntax error, unexpected ...세미콜론·따옴표 누락php -l로 줄 번호 확인
백지 화면, 로그도 없음Fatal error인데 출력이 꺼짐임시로 WP_DEBUG_DISPLAY true
설정 넣었는데 무반응wp-settings.php 아래에 define위쪽으로 이동

headers already sent는 특히 헷갈립니다. 원리는 이렇습니다. PHP는 HTTP 헤더를 먼저 보내고 본문을 보냅니다. 파일 끝 ?> 뒤에 눈에 안 보이는 공백이나 줄바꿈이 하나 있으면, 그게 본문 출력으로 처리됩니다. 그 뒤 워드프레스가 헤더를 보내려 하면 “이미 늦었다”는 에러가 납니다.

해결책은 단순합니다. PHP 파일 맨 끝의 ?>를 아예 지우는 겁니다. PHP는 닫는 태그가 없어도 파일 끝을 인식합니다. 워드프레스 코어 파일들도 대부분 ?> 없이 끝납니다. BOM 확인은 이렇게 합니다.

head -c 3 wp-config.php | xxd

efbbbf가 보이면 BOM이 있습니다. sed -i '1s/^\xEF\xBB\xBF//' wp-config.php로 제거할 수 있습니다.

반영이 안 될 때와 SSH가 막혔을 때

문법도 맞고 위치도 맞는데 변화가 없다면 캐시를 의심합니다. 순서는 캐시 플러그인 → 오브젝트 캐시(Redis, Memcached) → 호스팅사 서버 캐시 → 브라우저 캐시입니다. 특히 오브젝트 캐시는 DB 조회 결과를 메모리에 들고 있어서, 파일을 고쳐도 옛 값을 계속 내보낼 수 있습니다. Redis라면 redis-cli FLUSHALL로 한 번 비워보세요.

SSH가 막혀 있고 사이트도 안 열리는 최악의 상황이라면, 호스팅사 관리 패널의 파일 관리자로 들어갑니다. cPanel의 File Manager, 국내 호스팅사의 웹FTP 같은 기능입니다. 여기서 wp-config.php.bak.20250612를 wp-config.php로 되돌리면 됩니다. 브라우저만 있으면 되니, SSH 키를 못 챙긴 상황에서도 쓸 수 있습니다. 이 경로를 미리 확인해 두면 급할 때 시간을 아낍니다.

자주 묻는 질문

wp-config.php를 웹루트 밖으로 옮겨도 되나요?

됩니다. 웹루트 한 단계 위에 두면 워드프레스가 자동으로 찾습니다. 웹서버 설정이 잘못되어 PHP가 실행되지 않고 소스로 노출되는 사고를 막는 효과가 있습니다. 다만 실제 그런 사고는 드물고, 권한을 640으로 조정하고 .htaccess나 nginx 설정으로 접근을 막는 것만으로도 대부분 커버됩니다. 이관이나 백업 스크립트가 경로를 못 찾는 부작용도 있으니, 얻는 것과 잃는 것을 같이 보고 결정하세요.

보안 키를 바꾸면 기존 게시글이나 회원 데이터가 사라지나요?

사라지지 않습니다. SALT는 로그인 쿠키를 암호화하는 데만 쓰이고, 글이나 회원 정보와는 무관합니다. 비밀번호도 그대로 유효합니다. 유일한 영향은 로그인 중인 모든 세션이 끊기는 것입니다. 사용자들은 다시 로그인하면 됩니다. 다만 일부 플러그인이 SALT를 이용해 자체 데이터를 암호화하는 경우가 있어서, 결제나 API 연동 플러그인을 쓴다면 교체 후 해당 기능을 한 번 테스트해 보는 게 안전합니다.

DISALLOW_FILE_MODS와 DISALLOW_FILE_EDIT는 뭐가 다른가요?

DISALLOW_FILE_EDIT는 관리자 화면의 코드 편집기만 막습니다. 플러그인 설치와 업데이트는 그대로 됩니다. DISALLOW_FILE_MODS는 훨씬 강력해서, 플러그인·테마의 설치, 업데이트, 삭제까지 전부 막습니다. 자동 업데이트도 함께 멈춥니다. 관리형 호스팅이나 여러 사이트를 배포 파이프라인으로 관리하는 환경에서 씁니다. 일반 운영 사이트에서 DISALLOW_FILE_MODS를 켜면 보안 업데이트도 못 받게 되니 신중해야 합니다.

WP_MEMORY_LIMIT를 올렸는데 메모리 부족 에러가 그대로인 이유는?

서버 php.ini의 memory_limit가 상한이기 때문입니다. 워드프레스 설정은 그 안에서만 조절됩니다. php -i | grep memory_limit로 실제 값을 확인하세요. 웹에서 실행되는 값과 CLI 값이 다를 수 있어서, 관리자 화면의 도구 > 사이트 건강 > 정보 > 서버 항목에서 보는 게 더 정확합니다. php.ini를 못 고치는 공유 호스팅이라면 호스팅사에 요청해야 합니다. 그리고 정말 특정 플러그인이 메모리를 폭식하는 경우도 있으니, 한계를 올리기 전에 원인 플러그인을 찾는 편이 근본적입니다.

디버그 로그를 켜두면 속도가 느려지나요?

에러가 거의 없는 사이트라면 체감 차이는 거의 없습니다. 문제는 경고를 계속 뿜는 플러그인이 있을 때입니다. 페이지 요청마다 파일 쓰기가 수십 번 일어나면 디스크 I/O가 부담이 됩니다. 여기에 로그 파일이 수백 MB를 넘어가면 쓰기 자체가 느려집니다. SAVEQUERIES는 성격이 달라서 확실히 무겁습니다. 모든 쿼리를 메모리에 담으니 운영 서버에서는 절대 켜지 마세요.

호스팅사가 준 wp-config.php에 못 보던 define이 있는데 지워도 되나요?

지우기 전에 상수 이름으로 검색해 보세요. WP_CACHE는 캐시 플러그인, WP_REDIS_HOST는 Redis 오브젝트 캐시, WPCACHEHOME은 WP Super Cache가 넣는 값입니다. 호스팅사가 자체 최적화를 위해 넣은 값도 있습니다. 이름만 봐서 판단이 안 서면 지우지 말고 그냥 두는 게 맞습니다. 꼭 정리하려면 백업을 먼저 하고 앞에 //를 붙여 주석 처리한 뒤, 사이트가 정상인지 확인하고 나서 삭제하세요.

점검 체크리스트와 다음 할 일

자기 파일을 열어놓고 아래 순서대로 대조하면 한 번에 정리됩니다.

  1. 백업 — cp wp-config.php wp-config.php.bak.$(date +%Y%m%d) 실행했는지
  2. DB 4줄 — DB_NAME, DB_USER, DB_PASSWORD, DB_HOST 값이 맞는지
  3. SALT — 8개 키가 기본 문구가 아닌 무작위 값인지
  4. 파일 권한 — chmod 640 wp-config.php, ls -l로 -rw-r----- 확인
  5. 파일 편집기 — define('DISALLOW_FILE_EDIT', true); 들어갔는지
  6. 디버그 — WP_DEBUG true + WP_DEBUG_LOG true + WP_DEBUG_DISPLAY false 조합인지
  7. 리비전·휴지통 — WP_POST_REVISIONS, EMPTY_TRASH_DAYS 값 설정
  8. URL — 도메인 이전 이력이 있다면 WP_HOME/WP_SITEURL 확인
  9. 자동 업데이트 — WP_AUTO_UPDATE_CORE 정책 결정
  10. 문법 검사 — php -l wp-config.php에서 No syntax errors detected 확인

파일을 고쳤으면 반드시 세 가지 동작을 테스트하세요. 첫째, 프론트 페이지가 정상 로드되는지. 둘째, /wp-admin/에 로그인이 되는지. 셋째, 글을 하나 열어 수정하고 저장까지 되는지. 프론트만 보고 넘어가면 관리자 화면이 깨진 걸 며칠 뒤에 발견하는 일이 생깁니다.

정기 점검은 분기에 한 번 정도면 충분합니다. 디버그를 임시로 켰다가 끄는 걸 잊지 않았는지, 로그 파일이 비대해지지 않았는지 정도만 봐도 됩니다. 그리고 변경할 때마다 주석으로 날짜를 남기는 습관을 들이면 나중에 크게 도움이 됩니다.

// 2025-06-12 리비전 5개로 제한, posts 테이블 비대화 대응
define( 'WP_POST_REVISIONS', 5 );

git으로 관리한다면 wp-config.php는 .gitignore에 넣고, 비밀 값을 뺀 wp-config.sample.php만 커밋하는 방식이 일반적입니다. DB 비밀번호가 저장소에 올라가는 사고를 막을 수 있습니다.

다음으로 손볼 곳은 세 군데입니다. 퍼머링크와 접근 제어를 담당하는 .htaccess, wp-content/uploads를 포함한 디렉터리 권한 전체 정리, 그리고 DB와 파일을 정해진 시간에 자동으로 백업하는 스케줄 설정입니다. 백업 자동화는 crontab으로 처리하는데, 등록해도 안 돌아가는 함정이 꽤 많아서 crontab 작성법 정리, 등록해도 안 돌아가는 이유까지를 먼저 확인하고 시작하는 게 좋습니다.

댓글 3개

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다