PHP에서 가장 자주 만나는 오류 중 하나가 Warning: Cannot modify header information - headers already sent다. 결론부터 말하면, HTTP 헤더(쿠키·세션·리다이렉트)를 보내기 전에 이미 본문(HTML, 공백, 출력)이 먼저 전송되었기 때문이다. 원인을 알면 대부분 1분 안에 해결된다.
왜 발생하나
HTTP 응답은 헤더 → 본문 순서로 전송된다. PHP가 echo, HTML 한 줄, 심지어 공백 한 칸이라도 출력하는 순간 헤더 전송이 끝나버린다. 그 뒤에 header(), setcookie(), session_start() 같은 헤더 조작 함수를 호출하면 이 경고가 뜬다.
오류 메시지 끝에는 보통 원인 위치가 친절히 적혀 있다.
Warning: Cannot modify header information - headers already sent by
(output started at /var/www/html/config.php:1) in /var/www/html/login.php on line 12output started at config.php:1 이 부분이 실제로 출력을 시작한 파일과 줄 번호다. 여기를 먼저 본다.
가장 흔한 3가지 원인
| 원인 | 증상 | 해결 |
|---|---|---|
| 여는 태그 앞 공백/빈 줄 | <?php 앞에 공백 존재 | 파일 맨 앞 공백 제거 |
닫는 태그 ?> 뒤 줄바꿈 | include 파일 끝에 ?>\n | 닫는 태그 자체를 삭제 |
| BOM(UTF-8 with BOM) | 눈에 안 보이는 3바이트 | BOM 없는 UTF-8로 재저장 |
특히 ?> 뒤 줄바꿈과 BOM은 눈에 보이지 않아 가장 까다롭다.
해결 방법
1. include 파일의 닫는 태그를 없앤다
PHP만 있는 파일이라면 닫는 태그 ?>는 아예 쓰지 않는 것이 공식 권장 방식이다. 뒤에 실수로 붙은 공백·줄바꿈을 원천 차단한다.
<?php
// config.php — 닫는 태그(?>)를 쓰지 않는다
define('DB_HOST', 'localhost');
define('DB_NAME', 'mydb');
// 파일 끝. ?> 없음2. BOM 제거
리눅스에서 BOM이 있는지 바로 확인할 수 있다.
# 파일 첫 3바이트가 ef bb bf 면 BOM
head -c 3 config.php | xxd
# sed로 BOM 제거
sed -i '1s/^\xEF\xBB\xBF//' config.php3. 헤더는 출력보다 먼저
session_start()나 header()는 어떤 출력보다도 앞에 와야 한다.
<?php
session_start(); // 출력 전에 호출
header('Content-Type: text/html; charset=utf-8');
if (!isset($_SESSION['user'])) {
header('Location: /login.php');
exit; // 리다이렉트 뒤엔 반드시 exit
}4. 급할 때는 출력 버퍼링
구조를 당장 못 고치는 상황이라면 출력 버퍼링으로 임시 회피할 수 있다. 다만 근본 해결은 위 1~3번이다.
<?php
ob_start(); // 출력을 버퍼에 모았다가 마지막에 전송
// ... 코드 ...
ob_end_flush();점검 체크리스트
- [ ] 오류 메시지의
output started at위치를 먼저 확인했는가 - [ ] include 파일들의 닫는 태그
?>를 제거했는가 - [ ] 파일을 BOM 없는 UTF-8로 저장했는가
- [ ]
header()·session_start()를 모든 출력보다 앞에 두었는가 - [ ] 리다이렉트
header('Location: ...')뒤에exit;를 넣었는가
대부분은 닫는 태그 뒤 줄바꿈이나 BOM이 범인이다. 이 두 가지만 정리해도 같은 오류를 다시 만날 일이 크게 줄어든다.