web php

Headers

HTTP headers are metadata sent with requests and responses. They tell clients and servers how to interpret the body, whether to cache it, where to redirect, what cookies to store, and which security rules to apply.

In PHP, response headers must be set before output is sent. This is why stray whitespace, debugging echo, or accidental output before header() can break redirects, cookies, and content-type changes.

Setting response headers

Use header() for response headers and http_response_code() for the status code.

PHP example
<?php

declare(strict_types=1);

http_response_code(200);
header('Content-Type: application/json; charset=UTF-8');

echo json_encode(['ok' => true], JSON_THROW_ON_ERROR) . PHP_EOL;

// Output body:
// {"ok":true}

The example body prints in CLI, but headers only matter in an HTTP response. In a browser or API client, the Content-Type tells the client to treat the body as JSON.

Headers before body

Once PHP has sent body output, headers may already be committed.

PHP example
<?php

declare(strict_types=1);

echo 'Body started' . PHP_EOL;

echo headers_sent() ? 'headers sent' : 'headers not sent';
echo PHP_EOL;

// Prints:
// Body started
// headers sent

Keep response decisions before rendering. In larger apps, controllers usually decide status and headers before templates produce the body.

Common response headers

You will often set or inspect:

Content-Type      tells the client whether the body is HTML, JSON, text, etc.
Location          redirects the client to another URL
Set-Cookie        asks the browser to store a cookie
Cache-Control     controls caching behaviour
Content-Disposition suggests download behaviour for files

Security headers such as Content-Security-Policy, X-Frame-Options, and Strict-Transport-Security are important too, but they need careful configuration rather than random copy-paste.

Reading request headers

Some request headers are exposed through $_SERVER with an HTTP_ prefix.

PHP example
<?php

declare(strict_types=1);

$server = [
    'HTTP_ACCEPT' => 'application/json',
    'HTTP_USER_AGENT' => 'Example Browser',
];

echo 'Accept: ' . ($server['HTTP_ACCEPT'] ?? '*/*') . PHP_EOL;
echo 'User-Agent: ' . ($server['HTTP_USER_AGENT'] ?? 'unknown') . PHP_EOL;

// Prints:
// Accept: application/json
// User-Agent: Example Browser

Do not trust request headers automatically. Clients can send fake User-Agent, Referer, X-Forwarded-For, or Host values unless your infrastructure controls and validates them.

Cache And Download Headers

Headers often control browser behaviour rather than only body format.

For sensitive account pages, a response may need:

PHP example
<?php

declare(strict_types=1);

header('Cache-Control: no-store');

echo 'Account response prepared.' . PHP_EOL;

// Prints:
// Account response prepared.

For a generated download, use a controlled filename:

PHP example
<?php

declare(strict_types=1);

$filename = 'invoice-1001.pdf';

header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . $filename . '"');

echo $filename . PHP_EOL;

// Prints:
// invoice-1001.pdf

Never place raw user input directly into a response header. Newlines can create header-injection problems, and unsafe filenames can produce confusing download behaviour.

Debugging Header Problems

When PHP reports that headers were already sent, find the first output. Common causes are stray whitespace before <?php, a UTF-8 byte-order mark, a debug echo, or a warning rendered before the response headers.

headers_sent($file, $line) can show where output began. Fix the early output rather than hiding the warning with output buffering.

What you should be able to do

After this lesson, you should be able to set status codes and response headers, explain why headers must come before body output, use cache and download headers, debug early output, and treat request headers as untrusted input unless your server/proxy setup proves otherwise.

Practice

Task: Describe A JSON Response

Create a small PHP example or checklist for returning a JSON response.

Requirements

  • Set an HTTP status code.
  • Set a Content-Type: application/json; charset=UTF-8 header.
  • Encode a small response body with json_encode().
  • Include a note about why headers must be sent before body output.
  • Include a warning about trusting request headers.

Check your work

The example should make response metadata separate from the response body.

Show solution
PHP example
<?php

declare(strict_types=1);

http_response_code(201);
header('Content-Type: application/json; charset=UTF-8');

echo json_encode([
    'id' => 123,
    'status' => 'created',
], JSON_THROW_ON_ERROR) . PHP_EOL;

// Output body:
// {"id":123,"status":"created"}

The status code and Content-Type header describe the response. The JSON string is the body. Headers must be sent before output because once the body starts, PHP may no longer be able to change response metadata.

Request headers should be treated as untrusted input. A client can fake values such as User-Agent, Referer, Host, or forwarding headers unless the server and proxy configuration control them.