앱 API가 같이 도는 서버에서 WordPress 파일 안전하게 지우기

앱 백엔드도 함께 운영하는 공유 호스팅에서 WordPress 잔재 21개를 지운 기록. 지우기 전 꼭 볼 것, 되돌릴 수 있는 '옮긴 뒤 삭제' 절차, 410 Gone을 돌려주는 .htaccess.

블로그를 WordPress에서 다른 방식으로 옮겼는데 WordPress 파일이 서버에 그대로 남아 있는 분을 위한 글입니다. 저희 서버에는 모바일 앱 백엔드까지 함께 돌고 있었습니다. 결론은 이렇습니다. 롤백이 불가능해진 시점에 WordPress 파일은 지웁니다. 손으로 적은 목록대로 먼저 웹 루트 밖으로 옮기고, 회귀 점검이 통과한 뒤에야 완전히 삭제합니다. 그리고 옛 시스템 경로에는 410 Gone을 돌려주고, 새 주소가 있는 콘텐츠는 301을 유지합니다.

블로그는 2026년 11월 8일 WordPress에서 파이썬 정적 사이트 생성기로 옮겼습니다(앱스토어 등록 URL을 끊지 않고 옮긴 과정). 그때 .htaccess에서 WordPress 프런트 컨트롤러는 뺐지만, 롤백에 대비해 파일은 서버에 남겨 두었습니다. 11월 9일 WordPress 데이터베이스를 삭제하면서 파일만으로는 더 이상 사이트가 돌 수 없게 됐습니다. 남은 건 실제 DB 접속 정보가 적힌 wp-config.php를 포함한 쓸모없는 짐이자 공격 표면이었습니다.

"wp-* 다 지우기"가 위험한 이유

같은 문서 루트에서는 블로그 말고도 여러 서비스가 돕니다. 예를 들어 설치된 앱 16개가 실행될 때마다 호출하는 광고 설정 API가 있습니다. 실수 하나로 도메인 전체의 PHP가 멈추면 블로그는 문제 축에도 못 듭니다. 그래서 손대기 전에 네 가지를 확인했습니다.

1. WordPress 엔드포인트를 부르는 앱이 있는가. 플러터 앱 약 20개의 소스를 wp-content, wp-json, xmlrpc, wp-admin으로 grep했고 결과는 0건이었습니다. wp-content/uploads에서 이미지를 가져오거나 REST API를 쓰는 앱이 있었다면 그 경로는 남겨야 했을 겁니다.

2. auto_prepend_file이 걸려 있지 않은가. 가장 잊기 쉬운 함정입니다. WordPress 보안·방화벽 플러그인은 이런 줄이 들어간 .user.ini(또는 php.ini)를 만들곤 합니다.

auto_prepend_file = '/home/…/public_html/wp-content/…/firewall-bootstrap.php'

그러면 PHP는 그 디렉터리 아래의 모든 PHP 요청보다 이 파일을 먼저 실행합니다. WordPress 요청만이 아닙니다. 이 상태에서 wp-content를 지우면 앱 API를 포함한 도메인의 모든 PHP가 "Failed opening required" 치명적 오류로 멈춥니다. 저희 서버에는 .user.ini도 php.ini도 없어서 이 위험은 해당하지 않았습니다. 만약 있다면 먼저 그 설정 줄을 지우고, .user.ini 캐시가 만료될 때까지(보통 5분) 기다린 뒤에 가리키던 파일을 지우세요.

3. 루트 index.php가 정말 WordPress 것인가. 공유 호스팅에서는 WordPress와 무관한 index.php가 있을 수도 있습니다. 내려받아서 WordPress 테마 로더를 띄우는 WP_USE_THEMES 줄이 들어 있는지 확인했고, 이 확인이 실패하면 삭제 스크립트가 멈추도록 했습니다. 정적 사이트의 홈은 DirectoryIndex index.html index.php 설정으로 index.html이 먼저 나가고 있었기 때문에 영향이 없었습니다.

4. 백업 플러그인 폴더에 남길 게 있는가. wp-content 안에 이전용 플러그인의 백업 폴더가 있었지만, 실제 압축 파일은 없고 자리 표시용 파일뿐이었습니다. 남길 것은 없었습니다.

무엇이 남아 있었나

wp-admin, wp-includes, wp-content와 wp-*.php 말고도 이런 것들이 있었습니다.

항목 왜 문제인가
wp-config.php DB 호스트·사용자·비밀번호가 평문으로 들어 있음
xmlrpc.php 비밀번호 대입 공격과 핑백 악용의 단골 표적
license.txt, readme.html 스캐너에 WordPress 버전을 알려 줌
호스팅사가 만든 원클릭 자동 로그인 PHP 관리 패널의 "WordPress 로그인" 버튼이 만든 파일. WordPress 본체에는 없음
wp-content 안의 오브젝트 캐시·LiteSpeed 설정 더는 의미 없는 플러그인 설정

모두 21개 항목, 디렉터리 3개와 파일 18개였습니다. 자동 로그인 파일은 특히 챙겨 봐야 합니다. WordPress에 원래 들어 있는 파일이 아니라서 WordPress용 체크리스트에는 나오지 않습니다. 호스팅 관리 패널이 만든 파일이 문서 루트에 있는지 한 번 살펴보세요.

절차: 옮기고, 확인하고, 그다음 삭제

삭제 스크립트는 세 가지 원칙으로 만들었습니다.

  1. 손으로 적은 이름 목록. glob이나 wp-* 같은 패턴은 쓰지 않습니다. 공유 루트에서 패턴으로 지우면 몇 년 전에 누군가 만든 wp-backup-of-the-app-api 같은 폴더까지 날아갈 수 있습니다.
  2. 거부 목록. 목록에 보호 대상 이름(API 폴더, 앱 폴더, 언어 폴더, index.html, .htaccess, ads.txt, app-ads.txt)이 섞여 있지 않은지 맨 먼저 assert합니다. 확인하는 건 목록 자체뿐이지만, 실제로 사고가 나는 편집 실수는 이걸로 막을 수 있습니다.
  3. 삭제 대신 이름 바꾸기. 각 항목을 FTP RENAME으로 웹 루트 밖의 보관 폴더로 옮겼습니다. 파일이 수천 개인 디렉터리라도 이름 바꾸기는 즉시 끝나고, 그 순간 웹에서 사라지며, 다시 이름을 바꾸면 그대로 돌아옵니다.
DIRS  = ["wp-admin", "wp-content", "wp-includes"]
FILES = ["index.php", "wp-config.php", "xmlrpc.php", "license.txt", "readme.html", …]
NEVER = {"api", "app", "en", "ja", "ko", "static", "index.html", ".htaccess", "ads.txt", "app-ads.txt", …}

assert not (set(DIRS + FILES) & NEVER)
if b"WP_USE_THEMES" not in root_index_php:
    sys.exit("중단: 루트 index.php 가 WordPress 파일이 아님")
for name in targets:
    ftp.rename(f"{base}/{name}", f"{ARCHIVE}/{name}")

옮긴 뒤에는 사이트 URL 목록 점검과 API 응답 비교, 전체 회귀 점검을 돌렸습니다. 둘 다 통과한 다음에야 보관 폴더를 지웠습니다. 삭제는 백그라운드에서 MLSD로 트리를 따라가며 파일을 하나씩 지우는 방식입니다. FTP로는 wp-includes 하나만 해도 꽤 오래 걸리니, 사이트 상태를 아직 모를 때 할 일은 아닙니다.

.htaccess: 시스템 경로는 410, 콘텐츠는 301

파일이 사라지면 WordPress 경로 요청은 404로 떨어집니다. 이걸 명시적으로 처리하려고 기존 보안 블록 바로 뒤에 두 줄을 넣었습니다.

# WordPress 는 일부러 지웠다: 410 Gone
RewriteRule ^(wp-admin|wp-includes|wp-content|wp-json)(/|$) - [G,L]
RewriteRule ^(wp-[a-z-]+|xmlrpc)\.php$ - [G,L]

세부 사항입니다.

  • (/|$) 덕분에 앞부분만 같은 다른 이름은 걸리지 않습니다. /wpx-test/나, 가령 /wp-contentful/ 같은 경로는 대상이 아닙니다.
  • 둘째 줄은 .php에만 걸립니다. 그래서 /wp-sitemap.xml은 원래 있던 규칙에 도달해 /sitemap.xml로 301이 됩니다. 검색엔진이 기억하고 있을 수 있는 주소입니다.
  • 옛 글 URL, 카테고리, /feed/, /?p=ID는 새 페이지로 가는 301을 그대로 둡니다. "사라진" 게 아니라 "이사 간" 콘텐츠이기 때문입니다.
  • wp-content/uploads/ 아래의 옛 이미지 주소는 이제 410입니다. 외부 링크가 걸린 이미지를 옮겨 두었다면, 그 이미지의 301 규칙을 이 블록보다 앞에 두세요.

이 설정을 곧바로 운영에 넣지는 않았습니다. 생성한 .htaccess 전체를 격리된 샌드박스 하위 폴더에 두고 RewriteBase를 그 폴더로 바꾼 다음, 테스트 케이스 19개를 돌려 19/19 통과를 확인했습니다. 그중에는 /wpx-test/가 여전히 404인지, /xmlrpc.php에 POST를 보내도 410인지, 옛 uploads 이미지 경로가 410인지도 들어 있었습니다. 운영 반영 뒤에도 같은 상태 코드가 나왔고, 사이트 URL 점검 53/53, API 회귀 점검 실패 0건이었습니다.

예전 .htaccess 백업은 참고용으로 서버에 남겨 두었지만, 원래 있던 다음 규칙 때문에 웹에서는 읽을 수 없습니다.

RewriteRule (^|/)\.htaccess(\.|_)[^/]*$ - [F,L]

404냐 410이냐

Google 문서는 404와 410을 모두 "페이지 없음"으로 보고, 둘 다 색인에서 빠진다고 설명합니다. 410은 "일부러 지웠고 돌아오지 않는다"는 뜻을 더 분명하게 전하는 신호입니다. 410이 조금 더 빨리 빠진다는 이야기도 있지만, 저희는 재 보지 않았으니 그렇다고 말할 수 없습니다.

410을 고른 건 다른 이유, 우리 로그를 읽기 쉽게 하려고였습니다. /wp-login.php의 404는 깨진 링크인지, 오타인지, 스캐너인지 구별이 안 됩니다. 410이면 "일부러 지운 WordPress 경로"라는 게 바로 보입니다. 그렇게 해 두면 로그에 404가 찍혔을 때 그건 들여다볼 가치가 있는 것이 됩니다.

URL 종류 응답
옛 글, 카테고리, 피드, /?p=ID 새 페이지로 301
/wp-sitemap.xml /sitemap.xml로 301
wp-admin/, wp-includes/, wp-content/, wp-json/ 410
wp-*.php, xmlrpc.php (메서드 무관) 410
그 밖의 없는 주소 404

체크리스트

  1. 정말로 롤백이 불가능한지 확인한다(DB가 삭제됐는지). 되돌릴 가능성이 남아 있으면 파일은 그대로 둔다.
  2. 앱 소스를 wp-content, wp-json, xmlrpc, wp-admin으로 grep한다.
  3. .user.ini나 php.ini에 auto_prepend_file이 있는지 본다.
  4. 루트 index.php에 WP_USE_THEMES가 있는지, DirectoryIndex가 정적 홈을 먼저 내보내는지 확인한다.
  5. 호스팅사가 만든 파일까지 포함해 지울 항목을 손으로 적고, 보호 대상 이름과 대조한다.
  6. 전부 웹 루트 밖으로 이름을 바꿔 옮기고, 회귀 점검을 돌린 뒤에 삭제한다.
  7. 새 .htaccess는 샌드박스 폴더에서 먼저 시험하고, 운영 반영 후 같은 케이스를 다시 돌린다.

자주 묻는 질문

wp-content를 지우면 WordPress와 상관없는 PHP도 멈출 수 있나요?

네. .user.ini나 php.ini의 auto_prepend_file이 wp-content 안의 파일을 가리키고 있으면, 그 파일이 사라지는 순간 도메인의 모든 PHP 요청이 치명적 오류로 멈춥니다. 보안 플러그인이 자주 설정하므로 지우기 전에 반드시 확인하세요.

사이트를 옮긴 뒤에도 꼭 지워야 하는 WordPress 파일은?

DB 접속 정보가 든 wp-config.php, xmlrpc.php, 버전을 드러내는 license.txt와 readme.html, 호스팅사가 만든 자동 로그인 파일, 그리고 wp-admin·wp-includes·wp-content 폴더입니다. 이번에는 모두 21개 항목을 지웠습니다.

지운 WordPress 주소는 404와 410 중 무엇을 돌려줘야 하나요?

Google 문서에 따르면 둘 다 색인에서 빠집니다. 410 Gone은 '일부러 지웠다'는 뜻을 더 분명하게 전합니다. 이번에는 WordPress 시스템 경로에 410을 주고, 새 주소가 있는 글과 피드는 301을 유지했습니다.

  • #WordPress
  • #htaccess
  • #410
  • #보안
  • #공유호스팅
  • #FTP
  • #LiteSpeed

t.laboratory

직접 만들고 운영하는 앱·서버·서비스에서 겪은 문제와, 그 해결에 쓴 코드를 기록하는 기술 블로그입니다.

운영자 소개 · 앱

↑