PHPのheader()関数:初心者向けガイド

JavaScriptを有効にしてください

前書き

PHPにおけるheader()関数は、HTTPヘッダを操作するための強力なツールです。
この記事では、header()関数の基本的な使い方とその重要性を紹介します。

header()関数の基本

PHPのheader()関数は、生のHTTPヘッダを送信します。
この関数を使用すると、ブラウザに対して特定の命令を送信することができます。

基本的な使い方

PHP
1
2
<?php
header('Content-Type: text/plain');

このコードは、コンテンツタイプを「text/plain」に設定します。

例:HTTPヘッダの操作

リダイレクト

PHP
1
2
<?php
header('Location: https://example.com');

このコードは、ブラウザをhttps://example.comにリダイレクトします。

コンテンツタイプの設定

PHP
1
2
<?php
header('Content-Type: application/json');

このコードは、コンテンツタイプを「application/json」に設定します。
これは、JSONデータを出力するAPIを作成する際に便利です。

header()の注意点

header()関数は、どんな出力も送信される前に呼び出す必要があります。
出力後にheader()を呼び出すと、警告が発生します。
セキュリティと互換性を確保するために、適切なHTTPヘッダを設定することが重要です。

出力後にheader関数を呼び出した際の警告

もし何らかの出力(例えば、echoやHTMLタグ、空白や改行など)が既に行われた後にheader()関数を呼び出すと、PHPは「headers already sent(ヘッダーは既に送信された)」という警告を発生させます。

「headers already sent」警告

cmd
1
Warning: Cannot modify header information - headers already sent by (output started at /path/to/php/file.php:line) in /path/to/php/file.php on line XX

この警告メッセージには、次の情報が含まれます:

  1. 警告の種類:Cannot modify header information(ヘッダー情報を変更できない)
  2. 警告の理由:headers already sent(ヘッダーは既に送信された)
  3. 出力の開始場所:出力が開始されたファイルと行番号
  4. 警告発生場所:header()関数が呼び出されたファイルと行番号

警告の発生原因

  • 早期の出力:PHPファイルの先頭にある余分な空白や改行、echoやprintなどの出力関数の使用。
  • BOM(Byte Order Mark)の存在:UTF-8のBOMがファイルの先頭に含まれている場合。

解決策

  • 出力の遅延:header()関数を呼び出す前に、すべての出力を避けます。
  • 出力バッファリングの使用:ob_start()で出力バッファリングを開始し、header()関数の後でob_end_flush()やob_get_clean()を使用してバッファをフラッシュまたは取得します。
  • ファイルの確認:ファイルの先頭に余分な空白や改行がないことを確認し、BOMがないことを確認します。

出力バッファリングの使用例

こちらは少し複雑なので、実際のコード例を用いて解説します。

1. 出力バッファリングの有効化

出力バッファリングを有効にするには、スクリプトの最初にob_start()関数を呼び出します。
これにより、出力バッファリングが開始されます。

PHP
1
2
<?php
ob_start();

2. ヘッダの送信

バッファリングが有効な状態で、どのタイミングでも安全にヘッダを送信できます。

PHP
1
2
3
4
echo 'header関数呼び出し前';
header('Content-Type: text/plain');
echo "\n";
echo 'header関数呼び出し後';

header関数の前にテキストを出力していますが、ob_start関数でバッファリングしているため、まだブラウザに送信されていません。
そのため、echoの後にheader関数を使用してもheaders already sentの警告は出ません。

3. バッファのフラッシュ

最後に、出力バッファリングを終了し、バッファに溜まった内容をクライアント(ブラウザ)に送信するためにob_end_flush()を呼び出します。
または、バッファの内容を取得してからクリアするためにob_get_clean()を使用することもできます。

PHP
1
2
3
4
ob_end_flush();
// または
$content = ob_get_clean();
echo $content;

ここでob_end_flush関数を呼び出した時、またはob_get_clean関数で$content変数に先ほどの文字列が代入されechoでブラウザに送信される流れになります。

実際に試してみよう

ここからはdocker composeを使用してheader関数の実際の動作を確認してみましょう。

1. プロジェクトのディレクトリを作成

ターミナルで以下のコマンドを実行して、新しいディレクトリを作成します。

bash
1
2
mkdir php-header-test
cd php-header-test

2. docker-compose.yml の作成

docker-compose.yml を作成し、以下の内容を記述します。

docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
version: '3.8'

services:
  php:
    image: php:8.2-fpm
    container_name: header-test-php-container
    volumes:
      - ./html:/var/www/html

  nginx:
    image: nginx:latest
    container_name: header-test-nginx-container
    ports:
      - "8080:80"
    volumes:
      - ./html:/var/www/html
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php

3. Nginx の設定ファイルを作成

nginx ディレクトリを作成し、default.conf を作成します。

bash
1
mkdir nginx
default.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
server {
    listen 80;
    server_name localhost;
    root /var/www/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

4. PHP のフォームを作成

html ディレクトリを作成し、headerを出力する PHP ファイルを作成します。

bash
1
mkdir html
index.php
1
2
3
<?php
header('Content-Type: text/plain');
echo 'ヘッダーを出力しました';

5. フォルダ構成の確認

ここまでで下記のフォルダ構成になっていればOKです。

bash
1
2
3
4
5
6
7
8
9
$ tree php-header-test/
php-header-test/
├── docker-compose.yml
├── html
│   └── index.php
└── nginx
    └── default.conf

2 directories, 3 files

6. Docker Compose を起動

php-header-testフォルダで以下のコマンドを実行して、コンテナを起動します。

bash
1
docker-compose up -d

7. ヘッダーの出力を確認してみる

ブラウザで http://localhost:8080 にアクセスすると、「ヘッダーを出力しました」と表示されます。
しかし、実際にヘッダーが正しく出力されているかどうかを確認してみましょう。

7-1. 開発者ツールでヘッダーを確認

ブラウザの開発者ツールを使用して Content-Type のヘッダーを確認できます。以下の手順で確認してください。

  1. ブラウザで F12 キーを押して「開発者ツール」を表示し「Network」タブを選択する。
  2. 下記のように初回は何も情報が表示されないため、指示通りCtrl+Rを押下するかF5キーを押下しページを更新する。
    ブラウザの開発者ツールのNetworkタブ
  3. Network」タブの「Doc」タブをクリックする。
  4. 左側のペインで「localhost」を選択する。
  5. 右側に詳細ペインが表示されるので、「Headers」タブをクリックする。

📷 開発者ツールの Headers タブのスクリーンショット
ブラウザの開発者ツールのHeadersタブ

7-2. Content-Type ヘッダーの確認

「Headers」タブ内の「Response Headers」に Content-Type: という項目が text/plain;charset=UTF-8 となっていればちゃんと設定されています。
また、PHP によって charset=UTF-8 が追加されていることが分かります。

📷 Content-Type の表示例
Content-Typeを確認する

7-3. charset=UTF-8 が自動で追加される理由

charset=UTF-8 を明示的に設定していないのに勝手に追加されているのは、PHP の default_charset 設定が UTF-8 になっているため です。PHP は Content-Type を設定する際に、default_charset に基づいて charset=UTF-8 を自動で付加します。

7-4. charset=UTF-8 を削除するとどうなるか?

試しに charset を明示的に削除すると、ブラウザの表示が文字化けすることが確認できます。

以下のコードを実行してください。

index.php
1
2
3
4
<?php
ini_set('default_charset', ''); // PHPのデフォルトの文字コードを空に設定
header('Content-Type: text/plain'); // charset を明示的に指定しない
echo 'ヘッダーを出力しました';

📷 実行結果(文字化けの例)
charset未指定で文字化け

7-5. 文字化けの原因

なぜ文字化けするのか?
それは、PHP から出力される文字コードブラウザが表示時に解釈する文字コード が一致していないためです。

7-6. PHPの出力文字コードの確認

現在の PHP の出力文字コードを確認するには、以下のコードを実行します。

php
1
2
3
4
5
<?php
echo 'PHPのデフォルト文字コード: ' . ini_get('default_charset');
ini_set('default_charset', ''); // PHPのデフォルトの文字コードを空に設定
echo '</br>';
echo '空に設定後の文字コード: ' . ini_get('default_charset');
実行結果
1
2
PHPのデフォルト文字コード: UTF-8
空に設定後の文字コード:

通常は UTF-8 ですが、ini_set('default_charset', '') を適用すると空になります。

7-7. ブラウザの文字コードの確認

Chromeブラウザではどの文字コードでページを表示しているかは、「テキストエンコーディング」から分かります。
※Edgeではどこで確認できるか分からずでした…

ページ内の適当な場所で右クリックを押下し「テキストエンコーディング」を選択します。
ページのテキストエンコーディング

charsetを指定しないと「自動判別」になるようです。

今はもうほとんどの場合でUTF-8一択になっているため、文字コードで問題になることはないかと思いますが、上記のような仕様になっているんだなぁと知っておくと役に立つ時が来るかもしれません。

8. Locationでリダイレクトを発生させてみる

次に、Location ヘッダーを使用してリダイレクトを発生させてみましょう。

8-1. PHPファイルの作成

html フォルダに以下の3つのPHPファイルを作成してください。

redirect-index.php
1
2
<?php
echo '<a href="redirect-handler.php">リダイレクトしてみる?</a>';
redirect-handler.php
1
2
<?php
header('Location: http://localhost:8080/redirect-destination.php');
redirect-destination.php
1
2
3
4
<?php
echo 'リダイレクトされました。';
echo '<br>';
echo '<a href="/redirect-index.php">戻る</a>';

8-2. リダイレクトの確認

3つのPHPファイルを作成したら、http://localhost:8080/redirect-index.php にアクセスします。
表示された「リダイレクトしてみる?」のリンクをクリックすると、redirect-destination.php に遷移することが確認できるはずです。

また、開発者ツールの「Network」タブで確認すると、リダイレクトのステータスコードが 302 Found(一時的なリダイレクト)となっていることが分かります。

📷 リダイレクトされていることを確認
リダイレクトされていることを確認

8-3. リダイレクトのステータスコードを変更する

リダイレクトのステータスコードにはいくつか種類があります。
デフォルトでは 302 ですが、これは header 関数の第3引数を指定するか、http_response_code 関数を使うことで変更できます。

試しに、301 Moved Permanently(恒久的なリダイレクト)に変更してみましょう。

redirect-handler.php
1
2
3
4
<?php
// 下記の方法でも変更可能
// http_response_code(301);
header('Location: http://localhost:8080/redirect-destination.php', true, 301);

再度「Network」タブで確認すると、リダイレクトのステータスコードが 301 Moved Permanently になっていることが分かります。

📷 恒久的なリダイレクトになっていることを確認
恒久的なリダイレクトになっていることを確認

8-4. 301リダイレクトのキャッシュの影響

では、もう一度 301 リダイレクトを試してみましょう。
すると、ステータスコードが 200 となり、リダイレクトされていない ことが確認できます。

これは、301リダイレクトがブラウザにキャッシュされるため、2回目以降はリダイレクトせずに、直接 redirect-destination.php に遷移するためです。

📷 2回目はリダイレクトされていないことを確認
2回目はリダイレクトされていないことを確認

8-5. キャッシュを無効化する方法

テスト時にこのキャッシュが影響すると不便なため、キャッシュを無効化する方法 があります。
開発者ツールの「Network」タブにある 「Disable cache」 を有効にすると、キャッシュが適用されなくなります。

📷 「Disable cache」の設定
Disable cache

「Disable cache」にチェックを入れてから再度リダイレクトを試すと、301 リダイレクトが適用されることが確認できます。

📷 Disable cacheにチェックを入れるとリダイレクトされる
Disable cacheにチェックをいれるとリダイレクトされる

テストする際に結構ハマるポイントなのでキャッシュには注意するようにしてください。

9. jsonデータを出力してみる

ヘッダーのContent Typeapplication/jsonにしてjsonデータを生成し、HTML側から取得する手順を解説します。
PHP側では配列をjson形式に変換して出力し、HTML側ではfetch APIを使ってそのデータを取得・表示します。

9-1. PHPファイルでjsonデータを作成する

まずは、PHPファイルを作成して、jsonデータを出力する方法を見ていきましょう。

作成ファイル: json-data-api.php

以下のコードでは、PHPの連想配列を用いてユーザー情報を定義し、json_encode 関数でjson形式に変換しています。
また、Content-Type ヘッダーを application/json に設定することで、レスポンスがjsonデータであることを明示しています。

json-data-api.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?php
header('Content-Type: application/json; charset=UTF-8');

$data = [
    "id" => 6789,
    "name" => "山田 太郎",
    "email" => "taro.yamada@example.com",
    "phone" => "+81-xx-xxxx-xxxx",
    "address" => [
        "postal_code" => "100-0001",
        "country" => "Japan",
        "city" => "Tokyo",
        "street" => "千代田区1-2-3",
        "building" => "サンプルマンション 101号室"
    ]
];

echo json_encode($data, JSON_UNESCAPED_UNICODE);
解説
  • json_encode関数: PHPの連想配列 $data をjson形式に変換します。JSON_UNESCAPED_UNICODE オプションを使用することで、日本語などのマルチバイト文字をエスケープせずに出力できます。

9-2. HTMLファイルからjsonデータを取得する

次に、作成したPHPファイルからjsonデータを取得するためのHTMLファイルを作成します。
このHTMLファイルでは、JavaScriptの fetch API を利用して、PHPのjsonデータを取得し、画面上に整形して表示します。

作成ファイル: get-data.html
get-data.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>JSONデータの取得</title>
  <style>
    pre {
      background-color: #f4f4f4;
      padding: 1em;
      border: 1px solid #ddd;
    }
  </style>
</head>
<body>
  <h1>json-data-api.phpからJSONデータを取得する</h1>
  <div id="result">
    <p>データを取得中...</p>
  </div>

  <script>
    // fetch APIを利用してjson-data-api.phpからデータを取得
    fetch('json-data-api.php')
      .then(response => {
        if (!response.ok) {
          throw new Error('ネットワークエラーが発生しました。');
        }
        return response.json();
      })
      .then(data => {
        // 取得したデータを整形して表示
        const resultDiv = document.getElementById('result');
        resultDiv.innerHTML = '<h2>取得したデータ</h2>';
        const pre = document.createElement('pre');
        pre.textContent = JSON.stringify(data, null, 2);
        resultDiv.appendChild(pre);
      })
      .catch(error => {
        // エラー発生時の処理
        document.getElementById('result').innerHTML = `<p style="color: red;">エラー:${error.message}</p>`;
      });
  </script>
</body>
</html>
解説
  • fetch関数: 指定したURL(この場合は json-data-api.php)に対してHTTPリクエストを送信します。
  • response.ok: レスポンスが正常に受け取れたかどうかをチェックしています。
  • response.json(): レスポンスをjson形式に変換して返します。
  • JSON.stringify(data, null, 2): 取得したjsonデータを整形して見やすく表示しています。

9-3. 動作確認

http://localhost:8080/get-data.html にアクセスすると、json-data-api.phpに対してHTTPリクエストを送信し、jsonデータが表示されます。
jsonデータが表示される

レスポンスヘッダーにContent-Type: application/json; charset=UTF-8が設定されていることも確認できました。

10. 「headers already sent」警告を発生させてみる

この章では、実際に「headers already sent」警告を発生させて理解を深めます。
警告の原因や回避方法を確認していきましょう。

10-1. 「headers already sent」警告とは

改めておさらいですが、「headers already sent」警告は、HTTPヘッダーを送信する前に、既に何らかの出力が行われた場合に発生します。
ヘッダーはレスポンスの先頭に送信されるため、一度出力が開始されると後からヘッダーを変更することはできません。

10-2. 警告を発生させるファイルの作成

まず、htmlフォルダ内に headers-already-sent-warning.php を作成します。
以下のコードをファイルに貼り付け、意図的にヘッダー送信前に出力を行うことで、警告を発生させます。

headers-already-sent-warning.php
1
2
3
4
5
<?php
echo 'header関数呼び出し前';
header('Content-Type: text/plain');
echo '<br>';
echo 'header関数呼び出し後';

10-3. 警告の確認と影響

ブラウザで http://localhost:8080/headers-already-sent-warning.php にアクセスすると、
「headers already sent」警告が表示されます。また、Content-Typetext/plain に設定されていないことも確認できます。
なお、この警告はエラーではないため、処理は中断されず、後続の出力も実行されます。

「headers already sent」警告を意図的に発生させる

10-4. 出力バッファリングによる警告の回避

出力バッファリングを利用すると、出力が一時的にバッファに蓄えられるため、ヘッダー送信前に出力が行われても、バッファがフラッシュされるタイミングでまとめて出力されます。
これにより、ヘッダーが正しく設定され、警告を回避することができます。

htmlフォルダに headers-already-sent-no-warning.php ファイルを作成し下記コードを貼り付けます。

headers-already-sent-no-warning.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php
// 出力バッファリングを開始
ob_start();
echo 'header関数呼び出し前';
header('Content-Type: text/plain');
echo "\n";
echo 'header関数呼び出し後';

// バッファの内容をフラッシュして出力を送信
ob_end_flush();

10-5. バッファリング適用後の動作確認

ブラウザで http://localhost:8080/headers-already-sent-no-warning.php にアクセスすると、
警告が発生せず、Content-Typeも正しく text/plain に設定されていることが確認できます。

「headers already sent」警告を避ける

このように、出力バッファリングを活用することで、ヘッダー送信前の不要な出力を防ぎ、警告を回避できます。
また、警告は致命的なエラーではないため、処理自体は中断されずに継続されることも覚えておきましょう。

まとめ

PHPのheader()関数は、HTTPヘッダを操作し、ブラウザの振る舞いを制御するために不可欠です。
この記事で紹介した基本的な使い方と例を参考にして、あなたのPHPプログラミングに柔軟性と機能性を加えましょう。


スポンサーリンク

共有

もふもふ
著者
もふもふ
プログラマ。汎用系→ゲームエンジニア→Webエンジニア→QAエンジニア