PHP 리다이렉트와 캐시 문제 분석
캐시에 의한 웹 동작 차이 분석

목차
- 문제 상황 개요
- 정상 동작 프로세스 (최초 요청)
- 문제 발생 프로세스 (재요청)
- 원인 분석
- 핵심 개념 설명
- 해결 방안
1. 문제 상황 개요
- 테스트서버: 리다이렉트가 정상 동작
- 실서버: 최초 1회만 리다이렉트가 작동하고 이후 작동하지 않음
- 현상: 같은 코드이지만 환경에 따라 다른 동작을 보임
- 원인: 캐시 사용 여부에 따른 차이
2. 정상 동작 프로세스 (최초 요청)
메뉴의 다시보기 클릭 → "tv_sub/552/2636" 요청

- 캐시 파일이 없으므로
ob_start()
실행
- 응답 페이지 구성 시작 (헤더 + 바디)
- 헤더에 301 리다이렉트와 Location: "tv/552/" 포함
- 페이지 내용은 출력 버퍼에 저장됨
- 응답 시 헤더의 Location으로 리다이렉트 발생
- 동시에 출력 버퍼 내용은 캐시 파일로 저장됨
3. 문제 발생 프로세스 (재요청)
메뉴의 다시보기 클릭 → "tv_sub/552/2636" 재요청

- 캐시 파일이 존재함을 확인
- 캐시 파일을
echo
로 즉시 출력
exit
명령으로 이후 코드 실행 중단
- 이 과정에서 리다이렉트를 위한 헤더 설정 코드가 실행되지 않음
- 결과적으로 Location 헤더가 없어 리다이렉트 발생하지 않음
- 원래 요청한 "tv_sub/552/2636" 페이지가 그대로 표시됨
4. 원인 분석
핵심 원인:
- 캐시 사용 시
echo
로 내용 출력 후 exit
로 종료
- 이로 인해 리다이렉트 헤더가 설정되기 전에 응답이 완료됨
if (file_exists($cache_file)) {
echo file_get_contents($cache_file);
exit;
}
ob_start();
header("HTTP/1.1 301 Moved Permanently");
header("Location: tv/552/");
5. 핵심 개념 설명
PHP의 출력 버퍼링 (ob_start()
)
- 출력을 바로 전송하지 않고 버퍼에 저장
- 버퍼 내용은 스크립트 종료 시 또는 명시적 호출 시 전송
- 중요: 버퍼링은 출력 내용(body)만 제어하며 헤더는 제어하지 않음
헤더와 출력의 관계
- PHP에서
echo
등으로 출력이 시작되면 헤더도 함께 전송됨
- 헤더는 항상 본문(body) 이전에 전송되어야 함
- 출력이 시작된 후에는
header()
함수가 작동하지 않음
6. 해결 방안
가능한 해결책:
-
캐시 파일에 헤더 정보도 포함시키기
-
캐시 파일 출력 전에 헤더 설정하기
if (file_exists($cache_file)) {
header("HTTP/1.1 301 Moved Permanently");
header("Location: tv/552/");
echo file_get_contents($cache_file);
exit;
}
-
조건부 캐시 사용
- 리다이렉트가 필요한 페이지는 캐시를 사용하지 않거나
- 리다이렉트 정보를 별도로 확인하여 처리
질문에 대한 답변
"exit이 없으면 어떻게 되나요?"
exit
이 없어도 echo
가 실행되면 헤더가 이미 전송됨
- 이후
header()
함수는 효과가 없음 ("Headers already sent" 오류)
- 단, 이후 코드는 계속 실행되어 추가 출력이나 로직이 동작함
ob_start()
는 출력 내용(body)만 버퍼링함
- 헤더 정보는 별도로 관리되며 버퍼링되지 않음
- 출력 버퍼는 본문 콘텐츠만 제어할 뿐임
정리
- 문제 원인: 캐시 사용 시 리다이렉트 헤더를 설정하기 전에 출력과 종료가 발생
- PHP 특성: 출력 시작 시 헤더도 함께 전송됨
- 해결 방향: 캐시 처리 전에 필요한 헤더 설정 또는 캐시에 헤더 정보 포함
- 교훈: 출력 버퍼링과 헤더 관리는 별개의 메커니즘임을 이해해야 함
원본
[ 상황 정리 ]
테스트서버에서는 제대로 리다이렉트가 동작한다. 근데, 실서버에서는 리다이렉트가 최초 1회만 동작하고 제대로 동작하지않는다.
뭣도 모르고 됏다가 안됏다가 하는데 거의 안되네라고 생각했다. ㅎ.
1) 최초 요청 (캐시 동작 x, 실서버 기준)
- 메뉴의 다시보기를 클릭 -> "tv_sub/552/2636" 으로 요청이 된다.
- "tv_sub/552/2636" 에 진입해서 캐시가 없으면 ob_start 시작된다.
그와 동시에 요청에 따른 응답 페이지(응답할 헤더와 바디)를 구성한다. (header에 리다이렉트 301 요청과 Location ("tv/552/") 을 포함한다)
그리고 ob_start에 body의 내용들이 쌓이게 된다. (이때 header의 정보는 쌓이지 못한다)
- 페이지를 모두 구성하면 브라우저(Reqest)에 Response 한다. Response 하면 header에 Location이 값이 담겨 있기 때문에 바로 "tv/552/"로 리다이렉트
이와 동시에 캐시파일에도 해당 페이지의 내용이 담겨 저장된다.
2) 재 요청 (캐시 동작 o, 실서버 기준)
- 메뉴의 다시보기를 클릭 -> "tv_sub/552/2636" 으로 요청이 된다.
- 응답할 페이지를 구성하기 위해서 위에서부터 아래로 순차적으로 스캔하면서 상단에 캐시파일 로직을 지나는데 캐시파일이 존재하기 때문에
그냥 캐시파일을 echo로 뿌려버리고 exit 한다. 이렇게 되면 Response body와 header를 그 순간 그 상태로 응답해버린다. 그렇기 때문에 header에
리다이렉트를 해야할 Location 정보를 담지 못한다.
- 그래서 응답 페이지의 header에는 Location정보가 없어 리다이렉트 하지 않고, 그냥 요청한 "tv_sub/552/2636" 페이지가 캐시에 의해 응답된다.
[ 궁금한 점 ]
- 재요청 상황에서 echo로 뿌려버리고 exit 한다. 라고 작성 했는데 exit이 없으면? 어떻게?
-> php는 echo출력과 동시에 header 정보도 함께 응답해버린다. 그리고 exit을 해야 뒤에 내용들이 동작을 안한다. exit이 없어도 echo가 있으면 header 정보도 바로 응답이 되기 때문에 이후의 header()는 무용지물!
- ob_start()는 header 값을 담을 수 없나?
-> 출력 제어만 가능할 뿐이다. 원한다면 내용 출력이전에 header를 셋팅해라!