WordPress 보안 플러그인을 더 설치하기 전에 먼저 확인한 것은 “외부에서 실제로 접근할 필요가 있는 지점이 어디인가”였다. 이 사이트는 Windows host의 Docker Compose에서 WordPress, MariaDB와 Redis를 운영한다. 그래서 컨테이너 포트, 관리 화면, 설정 파일과 웹 루트의 임시 백업을 각각 나눠서 정리했다.
데이터베이스와 Redis는 host에 공개하지 않았다
MariaDB와 Redis 서비스에는 ports 항목을 두지 않았다. 두 서비스는 hongzi_blog_internal 네트워크 안에서만 WordPress와 통신한다. 데이터베이스 관리가 필요할 때도 인터넷에서 3306 포트로 직접 접속하지 않는다.
services:
db:
networks:
- internal
redis:
networks:
- internal
wordpress:
ports:
- "127.0.0.1:8088:80"
networks:
- internal
WordPress의 80번 포트도 모든 인터페이스가 아니라 host의 127.0.0.1에만 바인딩했다. 공개 HTTPS 요청은 Cloudflare Tunnel이 로컬 origin으로 전달한다. 이 구성에서는 외부 사용자가 host IP와 8088 포트로 바로 들어오는 경로를 만들지 않는다.
역방향 프록시 뒤의 HTTPS를 WordPress에 알려줬다
Tunnel과 같은 역방향 프록시가 앞에 있으면 방문자 구간은 HTTPS여도 컨테이너가 받는 origin 요청은 HTTP일 수 있다. WordPress가 이를 모르면 관리자 화면에서 리디렉션이 반복되거나 HTTP URL을 만들 수 있다. 그래서 X-Forwarded-Proto에 HTTPS가 포함되면 서버의 HTTPS 상태를 켜고, 공개 주소와 관리자 SSL을 명시했다.
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
$_SERVER['HTTPS'] = 'on';
}
define('WP_HOME', 'https://hongzi.us');
define('WP_SITEURL', 'https://hongzi.us');
define('FORCE_SSL_ADMIN', true);
WordPress 공식 문서도 관리자 로그인과 세션을 HTTPS로 강제할 때 FORCE_SSL_ADMIN을 사용하도록 안내하며, 역방향 프록시 환경에서는 원래 프로토콜 인식이 필요하다고 설명한다.
관리자 화면에서 PHP 파일 편집을 막았다
관리자 계정이 탈취됐을 때 테마·플러그인 편집기는 곧바로 PHP 코드 실행 통로가 될 수 있다. 이 사이트에서는 다음 값을 설정했다.
define('DISALLOW_FILE_EDIT', true);
define('WP_ENVIRONMENT_TYPE', 'production');
define('WP_DEBUG', false);
define('WP_DEBUG_DISPLAY', false);
커스텀 테마는 host 작업 폴더에서 관리하고 컨테이너에는 읽기 전용으로 마운트했다. PHP 업로드 설정 파일도 같은 방식으로 :ro를 사용한다. 관리자 화면이 아니라 검토 가능한 파일 변경으로 테마를 배포하려는 선택이다.
웹 루트에 남은 백업 파일을 따로 처리했다
속도 튜닝 과정에서 wp-config.php.bak, .htaccess.pre-w3tc.bak, object-cache.php.pre-w3tc.bak 같은 임시 파일이 생겼다. 내용이 정상 PHP가 아니거나 웹서버 설정이 달라지면 백업 파일이 텍스트로 노출될 가능성을 무시할 수 없다.
정리 스크립트는 다음 순서로 실행했다.
- 대상 파일을 웹 루트 밖의 host 백업 폴더로 복사한다.
- 컨테이너 원본과 host 복사본의 SHA-256을 비교한다.
- Apache 설정에 백업 확장자 접근 차단 규칙을 추가한다.
apache2ctl -t로 설정 문법을 확인한다.- 백업이 검증된 파일만 웹 루트에서 제거한다.
- 각 백업 URL이 HTTP 200을 반환하지 않는지 확인한다.
<FilesMatch "(^wp-config\.php|.*\.(bak|old|orig|save|swp)$)">
Require all denied
</FilesMatch>
2026년 7월 30일 작업 기록에는 네 개의 임시 백업을 host로 옮긴 뒤 SHA-256을 확인하고 웹 루트에서 제거한 결과가 남아 있다. 원래의 wp-config.php과 .htaccess는 제거 대상이 아니며 별도 사본만 보관했다.
인증 salt 교체는 검증과 롤백을 함께 묶었다
설정 정리 후에는 WordPress의 인증 key와 salt 여덟 개를 WP-CLI로 교체했다. 작업 전에 wp-config.php의 소유권·권한과 파일을 백업하고, 각 salt 줄의 지문만 비교했다. 교체 후에는 8개가 모두 바뀌었는지, PHP 문법과 홈 응답이 정상인지 확인했다. 중간 단계가 실패하면 이전 설정 파일을 복원하도록 만들었다.
salt를 바꾸면 기존 로그인 세션이 끊기는 것은 의도된 결과다. 따라서 방문이 적고 관리자가 다시 로그인할 수 있는 시간에 실행해야 한다.
보안은 한 번의 설정으로 끝나지 않는다
이 구성은 공격 가능성을 줄이는 방어층일 뿐 완전한 보안을 의미하지 않는다. WordPress 코어와 플러그인 업데이트, 관리자 계정 보호, Cloudflare 보안 로그 확인, 백업과 실제 복원 테스트가 계속 필요하다. 캐시와 성능 구성은 Redis 캐시와 이미지 최적화 기록에 별도로 정리했다.