EEltaCrew
블로그

301 은 되돌릴 수 없다: 서브도메인 이전 43분의 실수가 5일 뒤 404 로 돌아온 이야기, 그리고 Cloudflare 엣지 캐시

게시 2026-09-16실측 기준 2026-09-04읽는 시간 약 3분elta · EltaCrew

웹 서비스 두 개를 서브도메인으로 옮기던 날, 잘못된 301 이 43분간 나갔습니다. 서버는 바로 고쳤지만 브라우저는 5일 뒤에도 그 301 을 따라가 404 에 떨어졌습니다. permanent 리다이렉트를 내보내기 전에 해야 할 일, 이미 나간 301 을 구제하는 유일한 방법, 그리고 배포했는데 화면이 안 바뀌는 Cloudflare 엣지 캐시 사고를 씁니다.

무슨 일이 있었나

2026년 8월 31일, 자동 생성 페이지가 수만 장인 웹 서비스 두 개를 eltacrew.com/fixcode/, /benefit/ 에서 fixcode.eltacrew.com, benefit.eltacrew.com 으로 옮겼습니다. 옛 경로에는 301 을 걸었습니다.

첫 43분 동안 그 301 이 접두를 벗기지 않고 benefit.eltacrew.com/benefit/... 로 보냈습니다. Caddy 에서 uri strip_prefixredir 을 같은 블록에 두면 기본 순서상 redir 이 먼저 돌기 때문입니다. route {} 로 감싸 순서를 강제해야 합니다.

@moved path /benefit /benefit/*
handle @moved {
  route {
    uri strip_prefix /benefit
    redir https://benefit.eltacrew.com{uri} permanent
  }
}

43분 만에 고쳤고, curl 로 확인하면 정상이었습니다. 끝난 줄 알았습니다.

5일 뒤

9월 4일, 제 브라우저로 관리 대시보드의 링크를 눌렀더니 404 였습니다. 브라우저가 그 43분 안에 받은 301 을 캐시해 두고, 서버에 다시 묻지 않은 채 옛 목적지로 가고 있었습니다. 301 은 "영구"이므로 브라우저는 그 응답을 그대로 재사용합니다. 서버를 고쳐도 클라이언트는 모릅니다. 제 curl 은 매번 서버에 물으니 계속 정상이었고, 서버만 보고는 절대 잡을 수 없는 종류였습니다.

크롤러도 같이 캐시합니다. 사람 한 명의 문제로 보면 안 됩니다.

해법은 도착지에서 받아주는 것뿐

클라이언트 캐시를 지울 방법은 없습니다. 유일한 해법은 잘못 오는 주소를 도착지 쪽에서 받아주는 것입니다. 두 서브도메인에 /fixcode/*, /benefit/* 로 오면 접두를 한 번 더 벗겨 보내는 구제 리다이렉트를 추가했습니다. 하위 경로는 보존합니다.

앞으로의 순서

다른 사고: 배포했는데 화면이 안 바뀐다

같은 계열의 사고가 하나 더 있습니다. 8월 10일, 마크다운 변환기 웹에 설명 섹션을 추가해 배포했는데 화면에 안 보였습니다. index.html 은 최신인데 style.cssapp.js 만 Cloudflare 엣지에서 옛 파일로 내려오고 있었습니다. 응답 헤더가 cf-cache-status: HIT, Age: 6772, max-age=14400(4시간)이었습니다. 옛 CSS 에 body{overflow:hidden} 이 살아 있어서 설명 섹션이 HTML 에는 있는데 스크롤이 막혀 안 보였던 것입니다.

진단 순서는 이렇습니다.

  1. origin 서버의 실제 파일 해시
  2. 라이브 URL 의 파일 해시
  3. cf-cache-statusAge 헤더

origin 이 최신인데 라이브가 다르면 엣지 캐시입니다. 해법은 CSS·JS 를 고칠 때마다 ?v= 쿼리를 올리는 것입니다. HTML 은 캐시가 짧아 먼저 갱신되고 자산만 남는 조합이 제일 헷갈립니다. 서브 경로 사이트의 파비콘도 같은 이유로 4시간 동안 옛 파일이 보입니다.

여기서도 오판이 하나 있었습니다. 해시가 달라 "낡았다"고 볼 뻔한 파일 두 개는 CRLF 차이일 뿐 내용이 같았습니다. Windows 에서 core.autocrlf=truegit archive 가 CRLF 로 출력합니다. tr -d '\r' 후에 비교해야 판별됩니다.

정리

배포HTTPCloudflareCaddy

함께 읽기

한 페이지에 한 언어: IP 국가 판정과 한/영 병기를 버리고 브라우저 언어 + hreflang 으로 바꾼 이유웹 서비스 6개의 초기 언어를 정하는 방식이 제각각이었습니다. IP 국가로 정하던 곳은 구글봇에게만 영어가 나가 검색 결과 언어를 통제할 수 없었고, 한/영을 한 페이지에 병기하던 랜딩은 구글이 기대하는 "페이지 하나에 언어 하나" 와 어긋났습니다. 전 웹을 한 규칙으로 통일한 과정을 씁니다.