Cara Mengatasi Headers Already Sent di PHP

Headers already sent terjadi ketika PHP sudah mengirim output ke browser, lalu kode masih mencoba mengubah header HTTP dengan header(), setcookie(), atau session_start(). Solusi cepatnya: pindahkan session_start(), header(), dan setcookie() ke bagian paling atas sebelum HTML, spasi, echo, atau include yang menghasilkan output.

Error lengkapnya sering terlihat seperti ini:

Warning: Cannot modify header information - headers already sent by (output started at ...)

Bagi pemula, pesan ini membingungkan karena yang terlihat hanya fungsi header() atau session_start(). Padahal sumber masalahnya sering berada beberapa baris sebelumnya, bahkan bisa berasal dari file lain yang di-include.

Kenapa Header Harus Dikirim Lebih Dulu?

Saat browser menerima halaman, server mengirim dua bagian:

1. Header HTTP
2. Body HTML

Header berisi informasi seperti status response, cookie, lokasi redirect, dan tipe konten. Body berisi HTML yang tampil di layar.

Begitu PHP mengirim body, header dianggap selesai. Setelah itu PHP tidak bisa lagi berkata "redirect ke halaman lain" atau "buat cookie baru".

Contoh yang salah:

<!DOCTYPE html>
<html>
<body>
<?php
header('Location: dashboard.php');
exit;
?>
</body>
</html>

HTML sudah keluar duluan, sehingga redirect gagal.

Penyebab Paling Umum

PenyebabContoh
Ada HTML sebelum header()<!DOCTYPE html> ditulis sebelum redirect
Ada echo atau var_dump()Debug output muncul sebelum header
Ada spasi sebelum <?phpFile PHP dimulai dengan baris kosong
File include menghasilkan outputconfig.php menampilkan teks atau error
session_start() terlambatDipanggil setelah template HTML
File tersimpan dengan BOMAda karakter tersembunyi sebelum kode PHP

Solusi Utama: Pindahkan Logika ke Atas

Untuk halaman login, proses redirect harus dilakukan sebelum HTML ditampilkan.

<?php
session_start();

if (isset($_SESSION['user_id'])) {
    header('Location: dashboard.php');
    exit;
}

$error = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';

    if ($username === 'admin' && $password === 'secret') {
        $_SESSION['user_id'] = 1;
        header('Location: dashboard.php');
        exit;
    }

    $error = 'Username atau password salah.';
}
?>

<!DOCTYPE html>
<html lang="id">
<body>
    <?php if ($error): ?>
        <p><?= htmlspecialchars($error) ?></p>
    <?php endif; ?>

    <form method="POST">
        <input type="text" name="username">
        <input type="password" name="password">
        <button type="submit">Login</button>
    </form>
</body>
</html>

Pola ini aman karena semua operasi header selesai sebelum HTML dikirim.

Solusi untuk session_start()

session_start() juga mengirim header cookie session. Karena itu, posisinya harus sangat awal.

Salah:

<h1>Dashboard</h1>
<?php
session_start();

Benar:

<?php
session_start();

if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}
?>

<h1>Dashboard</h1>

Jika session kamu tetap bermasalah setelah posisi session_start() benar, baca panduan Kenapa Session PHP Tidak Tersimpan?.

Solusi untuk File Include

Masalah sering berasal dari file yang di-include, misalnya config.php.

Salah:

<!-- config.php -->
<?php
$dbHost = 'localhost';

Komentar HTML di atas sudah dianggap output. Jika config.php di-include sebelum header(), redirect bisa gagal.

Benar:

<?php
$dbHost = 'localhost';
$dbName = 'belajar_web';

Untuk file PHP murni yang hanya berisi konfigurasi atau function, kamu juga boleh tidak menutup dengan ?> agar tidak ada spasi tidak sengaja di akhir file.

<?php
return [
    'db_host' => 'localhost',
    'db_name' => 'belajar_web',
];

Solusi untuk Redirect Setelah Simpan Data

Pola umum setelah submit form adalah Post/Redirect/Get. Setelah data berhasil disimpan, redirect user ke halaman lain agar form tidak terkirim ulang saat refresh.

<?php
require 'config/database.php';
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $nama = trim($_POST['nama'] ?? '');

    if ($nama !== '') {
        $stmt = $pdo->prepare('INSERT INTO kategori (nama) VALUES (?)');
        $stmt->execute([$nama]);

        $_SESSION['flash'] = 'Kategori berhasil ditambahkan.';
        header('Location: kategori.php');
        exit;
    }
}
?>

<form method="POST">
    <input type="text" name="nama">
    <button type="submit">Simpan</button>
</form>

Perhatikan bahwa tidak ada HTML sebelum blok proses POST.

Cara Menemukan Output yang Terlanjur Keluar

Pesan error biasanya memberi petunjuk:

output started at /path/file.php:12

Buka file dan baris tersebut. Cari:

  • teks di luar <?php ?>
  • spasi atau baris kosong sebelum <?php
  • echo, print_r(), var_dump()
  • warning PHP lain yang tampil sebelum redirect
  • file include yang menampilkan HTML

Jika output berasal dari warning PHP lain, perbaiki warning itu dulu. Untuk dasar penanganan error, baca Error Handling.

Output Buffer sebagai Solusi Sementara

PHP punya output buffering dengan ob_start(). Ini menahan output sementara sebelum dikirim ke browser.

<?php
ob_start();
session_start();

echo "Output ini ditahan dulu.";

header('Location: dashboard.php');
exit;

Namun untuk pemula, jangan jadikan ini solusi utama. Lebih baik rapikan struktur file: proses dulu, tampilkan HTML belakangan. Output buffer berguna untuk kasus tertentu, tetapi bisa menutupi struktur kode yang berantakan.

Pola Struktur Halaman yang Aman

Gunakan urutan ini untuk halaman PHP biasa:

1. require config dan helper
2. session_start()
3. proses form, validasi, redirect
4. ambil data dari database
5. tampilkan HTML

Contoh:

<?php
require 'config/database.php';
require 'helpers/auth.php';

session_start();

if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}

$stmt = $pdo->query('SELECT * FROM produk ORDER BY id DESC');
$produkList = $stmt->fetchAll();
?>

<!DOCTYPE html>
<html lang="id">
<body>
    <h1>Daftar Produk</h1>
</body>
</html>

Jika project makin besar, pertimbangkan pola Front Controller Pattern agar setup session, database, dan middleware tidak tercecer di banyak file.

Kesalahan yang Sering Terjadi

1. Menaruh session_start() di dalam template

Template biasanya sudah berisi HTML. Pindahkan session_start() ke file utama paling atas.

2. Debug dengan echo sebelum redirect

<?php
echo "Login berhasil";
header('Location: dashboard.php'); // Gagal

Jika butuh debug, gunakan log:

<?php
error_log('Login berhasil untuk user ID 1');
header('Location: dashboard.php');
exit;

3. Lupa exit setelah header('Location: ...')

header() hanya mengirim instruksi redirect. Kode di bawahnya masih bisa berjalan jika tidak dihentikan.

<?php
header('Location: login.php');
exit;

4. Ada spasi sebelum tag pembuka PHP

Salah:


<?php
session_start();

Benar:

<?php
session_start();

5. File include menutup PHP lalu menyisakan spasi

Untuk file konfigurasi, helper, atau class, tidak wajib menulis tag penutup ?>.

FAQ

Apakah headers already sent selalu karena header()?

Tidak. Error ini juga bisa muncul karena session_start() atau setcookie(), karena keduanya perlu mengirim header HTTP.

Kenapa error menunjuk file yang berbeda?

Karena output pertama bisa berasal dari file include. Misalnya login.php memanggil config.php, lalu config.php punya spasi sebelum <?php.

Apakah ob_start() aman digunakan?

Aman untuk kasus tertentu, tetapi jangan dijadikan kebiasaan pertama. Struktur kode yang benar lebih mudah dipelihara.

Apakah file PHP harus ditutup dengan ?>?

Untuk file PHP murni, tidak harus. Banyak developer sengaja tidak menutupnya untuk mencegah spasi tidak sengaja setelah tag penutup.

Bagaimana cara redirect yang benar setelah login?

Panggil session_start() di atas, validasi login sebelum HTML, set $_SESSION, panggil header('Location: ...'), lalu exit.

Artikel Terkait