PHP 개발을 하다가 갑자기 화면에 "Warning: Cannot modify header information - headers already sent by ..."라는 에러 메시지가 출력되며 페이지 이동이나 세션 처리(session_start)가 실패하는 경험을 해봤을 것이다. 다만 대부분의 개발자들은 이 에러가 발생하는 정확한 원인이나 HTTP 프로토콜의 작동 방식을 모른 채, 눈앞의 공백이나 echo 문을 대충 지워보며 넘어가는 경우가 많다. 이번에는 이 에러가 정확히 왜 발생하며, 원인별 대처 방법과 실무에서 바로 적용할 수 있는 해결책을 완벽하게 정리해서 소개하겠다.

 

1. HTTP 헤더 전송 원리와 에러 발생 이유

웹 서버와 브라우저가 통신할 때 응답(Response) 데이터는 크게 두 부분으로 나뉜다. 첫 번째는 쿠키 설정, 리다이렉트 위치, 콘텐츠 타입 등을 포함하는 HTTP 헤더(Header) 부분이고, 두 번째는 실제 화면에 출력되는 HTML, JSON 등의 HTTP 바디(Body) 부분이다.
HTTP 규격상 서버는 반드시 바디 데이터를 전송하기 전에 헤더 데이터를 먼저 전송해야 한다. PHP 스크립트 실행 중 단 한 글자의 문자열이라도 출력(echo, print, HTML 태그, 파일 앞뒤 공백 등)되면, PHP는 이미 바디 출력이 시작되었다고 판단하여 쌓여 있던 HTTP 헤더를 브라우저로 전송해 버린다. 그 이후에 PHP 코드에서 header()setcookie(), session_start() 처럼 헤더를 수정하려고 하면 바로 "headers already sent" 경고가 발생한다.

 

2. headers already sent 에러의 4가지 주요 원인

이 에러가 출력될 때 메시지를 잘 보면 headers already sent by (output started at /path/file.php:12) 처럼 어디서 출력이 시작되었는지가 명확히 적혀 있다. 주요 원인은 다음과 같다.

1. PHP 태그 전후의 공백 및 줄바꿈: <?php 태그 앞에 빈 줄이 있거나, 닫는 태그 ?> 뒤에 줄바꿈이 있는 경우
2. header() 실행 전 출력문 존재: echo, print, var_dump 혹은 HTML 코드 실행 후 헤더 함수 호출
3. UTF-8 BOM(Byte Order Mark) 포함: 파일 저장 시 에디터가 숨겨진 BOM 문자를 파일 맨 앞에 삽입한 경우
4. 포함된 외부 파일(include/require)에서의 출력: 메인 파일 이전에 불러온 파일에서 출력이 발생한 경우

 

3. 실전 예제로 보는 코드 비교 및 해결법

실무에서 자주 실수하는 상황별 코드 패턴을 살펴보자.

 

패턴 A: header() 호출 이전 출력문 작성 실수

✗ 잘못된 코드 (header 호출 전 echo 실행)

<?php
echo "로그인 처리 중입니다...";

// 출력이 이루어진 후 헤더 변경 시도 -> 에러 발생!
header("Location: /dashboard.php");
exit;
?>

✓ 올바른 코드 (모든 로직과 헤더 처리 후 출력)

<?php
// 헤더 변경이나 세션 처리는 출력문보다 먼저 실행
header("Location: /dashboard.php");
exit;
?>

[실행 결과/출력값]
잘못된 코드 실행 시: Warning: Cannot modify header information - headers already sent by (output started at /var/www/index.php:2) 발생 및 페이지 이동 실패.
올바른 코드 실행 시: 에러 없이 정상적으로 /dashboard.php로 리다이렉트됨.

 

패턴 B: 출력 버퍼링(Output Buffering) 활용

로직 구조상 어쩔 수 없이 중간에 출력이 발생하거나, 템플릿 처리 시 미리 출력을 방지해야 하는 경우에는 PHP의 Output Buffering(ob_start()) 기능을 활용할 수 있다.

✓ 올바른 코드 (출력 버퍼링 사용)

<?php
// 출력 버퍼링 시작 (브라우저로 즉시 보내지 않고 버퍼에 대기)
ob_start();

echo "임시 데이터 출력";

// 버퍼가 활성화되어 있으므로 header() 사용 가능
header("Content-Type: application/json; charset=utf-8");

$data = array("result" => "success");
// 버퍼를 비우고 출력 데이터 세팅
ob_clean();
echo json_encode($data);
ob_end_flush();
?>

[실행 결과/출력값]
{"result":"success"} (에러 없이 올바른 Content-Type 헤더와 함께 JSON 응답 전달)

 

4. 상황별 원인 및 해결 방법 한눈에 보기

다음 표는 headers already sent 에러가 발생하는 대표적 원인과 해결 방법을 비교한 표이다.

발생 원인 잘못된 형태 (✗) 올바른 해결 방법 (✓)
태그 외 공백 <?php 또는 ?>
PHP 전용 파일은 닫는 태그(?>)를 생략하거나 공백 제거
파일 인코딩 UTF-8 with BOM 형식 저장 에디터(VSCode 등)에서 인코딩을 UTF-8 (BOM 없음)으로 변경
세션/헤더 위치 HTML 태그 중간에 session_start() 최상단 1번째 줄로 위치 변경
설정 차이 output_buffering = Off php.ini에서 output_buffering = 4096 설정 활성화 고려

 

5. 마무리 및 정리

PHP headers already sent 에러 해결은 웹 서버와 클라이언트 간의 HTTP 통신 흐름을 이해하는 첫걸음이다. 작은 공백 하나나 파일 인코딩 설정 습관이 모여 안정적인 웹 애플리케이션을 만든다는 점을 잊지 말자. 이 글의 원인 분석 및 체크리스트를 참고해 프로젝트 내 파일들의 시작 지점과 헤더 호출 순서를 점검해 본다면, 더 이상 헤더 전송 에러로 시간을 허비하지 않고 깔끔하게 해결할 수 있을 것이다.