As we've already discussed, the HTTP response that a server sends back to a client contains headers that identify the type of content in the body of the response, the server that sent the response, how many bytes are in the body, when the response was sent, etc. PHP and Apache normally take care of the headers for you, identifying the document as HTML, calculating the length of the HTML page, and so on. Most web applications never need to set headers themselves. However, if you want to send back something that's not HTML, set the expiration time for a page, redirect the client's browser, or generate a specific HTTP error, you'll need to use the header( ) function.
The only catch to setting headers is that you must do so before any of the body is generated. This means that all calls to header( ) (or setcookie( ), if you're setting cookies) must happen at the very top of your file, even before the <html> tag. For example:
<?php header('Content-Type: text/plain'); ?> Date: today From: fred To: barney Subject: hands off! My lunchbox is mine and mine alone. Get your own, you filthy scrounger!
Attempting to set headers after the document has started results in this warning:
Warning: Cannot add header information - headers already sent
The Content-Type header identifies the type of document being returned. Ordinarily this is "text/html", indicating an HTML document, but there are other useful document types. For example, "text/plain" forces the browser to treat the page as plain text. This type is like an automatic "view source," and it is useful when debugging.
In Chapter 9 and Chapter 10, we'll make heavy use of the Content-Type header as we generate documents that are really graphic images and Adobe PDF files.
To send the browser to a new URL, known as a redirection , you set the Location header:
<?php header('Location: http://www.example.com/elsewhere.html'); exit( ); ?>
If you provide a partial URL (e.g., "/elsewhere.html"), the redirection is handled internally by the web server. This is only rarely useful, as the browser generally won't learn that it isn't getting the page it requested. If there are relative URLs in the new document, the browser will interpret them as being relative to the document it requested, not the document it was sent. In general, you'll want to redirect to an absolute URL.
A server can explicitly inform the browser, and any proxy caches that might be between the server and browser, of a specific date and time for the document to expire. Proxy and browser caches can hold the document until that time or expire it earlier. Repeated reloads of a cached document do not contact the server. However, an attempt to fetch an expired document does contact the server.
To set the expiration time of a document, use the Expires header:
header('Expires: Fri, 18 Jan 2002 05:30:00 GMT');
To expire a document three hours from the time the page was generated, use time( ) and gmstrftime( ) to generate the expiration date string:
$now = time( ); $then = gmstrftime("%a, %d %b %Y %H:%M:%S GMT", $now + 60*60*3); header("Expires: $then");
To indicate that a document "never" expires, use the time a year from now:
$now = time( ); $then = gmstrftime("%a, %d %b %Y %H:%M:%S GMT", $now + 365*86440); header("Expires: $then");
To mark a document as already expired, use the current time or a time in the past:
$then = gmstrftime("%a, %d %b %Y %H:%M:%S GMT"); header("Expires: $then");
This is the best way to prevent a browser or proxy cache from storing your document:
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache");
For more information on controlling the behavior of browser and web caches, see Chapter 6 of Web Caching, by Duane Wessels (O'Reilly).
HTTP authentication works through request headers and response statuses. A browser can send a username and password (the credentials ) in the request headers. If the credentials aren't sent or aren't satsifactory, the server sends a "401 Unauthorized" response and identifies the realm of authentication (a string such as "Mary's Pictures" or "Your Shopping Cart") via the WWW-Authenticate header. This typically pops up an "Enter username and password for ..." dialog box on the browser, and the page is then re-requested with the updated credentials in the header.
To handle authentication in PHP, check the username and password (the PHP_AUTH_USER and PHP_AUTH_PW elements of $_SERVER) and call header( ) to set the realm and send a "401 Unauthorized" response:
header('WWW-Authenticate: Basic realm="Top Secret Files"'); header("HTTP/1.0 401 Unauthorized");
You can do anything you want to authenticate the username and password; for example, you could consult a database, read a file of valid users, or consult a Microsoft domain server. This example checks to make sure that the password is the username, reversed:
$auth_ok = 0; $user = $_SERVER['PHP_AUTH_USER']; $pass = $_SERVER['PHP_AUTH_PW']; if (isset($user) && isset($pass) && $user === strrev($pass)) { $auth_ok = 1; } if (!$auth_ok) { header('WWW-Authenticate: Basic realm="Top Secret Files"'); header('HTTP/1.0 401 Unauthorized'); }
Putting this into a document gives something like:
<?php $auth_ok = 0; $user = $_SERVER['PHP_AUTH_USER']; $pass = $_SERVER['PHP_AUTH_PW']; if (isset($user) && isset($pass) && $user === strrev($pass)) { $auth_ok = 1; } if (!$auth_ok) { header('WWW-Authenticate: Basic realm="Top Secret Files"'); header('HTTP/1.0 401 Unauthorized'); // anything else printed here is only seen if the client hits "Cancel" } ?> }<!-- your password-protected document goes here -->
If you're protecting more than one page, put the above code into a separate file and include it at the top of every protected page.
Copyright © 2003 O'Reilly & Associates. All rights reserved.