PHP header()函数及其常见使用
在Web开发中,HTTP头信息(HTTP Headers)决定了浏览器如何处理响应内容、缓存策略、重定向行为甚至身份认证机制。PHP的header()函数是控制HTTP头的核心工具,通过它我们可以实现页面重定向、设置内容类型、管理缓存、实现HTTP认证等功能。本文将深入解析header()函数的工作原理、常见使用场景、最佳实践及典型问题的解决方案,帮助开发者更安全、高效地使用该函数。
目录#
- header()函数基础
- 函数定义与语法
- 工作原理
- 参数详解
- 常见使用场景
- 页面重定向(Location头)
- 设置响应头(Content-Type、Content-Disposition等)
- 缓存控制(Cache-Control、Expires等)
- HTTP基本认证
- 自定义响应状态码
- 最佳实践
- 输出前调用header()
- 错误处理与headers_sent()
- Location头的安全处理
- 内容安全与HTTP头
- 常见问题及解决方案
- “Headers already sent”错误
- 重定向后代码继续执行
- 响应状态码不生效
- 总结
- 参考资料
1. header()函数基础#
1.1 函数定义与语法#
header()是PHP的内置函数,用于向客户端(通常是浏览器)发送原生的HTTP头信息。HTTP头在响应的实际内容(如HTML、JSON数据)之前发送,因此header()必须在任何输出(包括空格、HTML标签、echo/print语句等)之前调用。
函数语法:
header(string $header, bool $replace = true, int $http_response_code = null): void- $header:要发送的HTTP头内容(如
"Location: https://example.com"、"Content-Type: application/json")。 - $replace(可选):是否用新的头替换同类型的现有头。默认为
true(替换),若设为false,则新头会添加到现有头后。 - $http_response_code(可选):强制设置HTTP响应状态码(如
404、200)。若提供此参数,PHP会自动发送对应的状态码头。
1.2 工作原理#
HTTP协议要求响应头必须在响应体(如HTML内容)之前发送。当PHP执行到header()时,它会将头信息暂存,直到脚本执行结束或输出开始(如调用echo、加载模板等),此时PHP会先发送所有暂存的头,再发送响应体。
若在header()调用前已有输出(即使是一个空格或BOM头),PHP会抛出**“Headers already sent”**错误,因为HTTP头的发送时机已过。
1.3 参数详解#
(1)$header参数#
需遵循HTTP头的格式,例如:
- 重定向头:
"Location: https://example.com"(需配合状态码301/302等)。 - 内容类型头:
"Content-Type: application/json; charset=utf-8"。 - 缓存控制头:
"Cache-Control: no-cache"。
(2)$replace参数#
示例:
// 替换现有Content-Type头(默认replace=true)
header("Content-Type: text/plain", true);
// 添加新的X-Custom-Header(replace=false,不替换同类型头)
header("X-Custom-Header: value1", false);
header("X-Custom-Header: value2", false); // 最终会有两个X-Custom-Header头(3)$http_response_code参数#
示例:
// 方式1:直接发送状态码头
header("HTTP/1.1 404 Not Found");
// 方式2:通过参数显式设置状态码
header("Content-Type: text/html", true, 404);2. 常见使用场景#
2.1 页面重定向(Location头)#
作用:将用户浏览器跳转到新的URL,常用于登录后跳转、404页面重定向、多语言切换等。
示例1:临时重定向(302)#
// 用户未登录,跳转到登录页
if (!isset($_SESSION['user_id'])) {
// 302表示临时重定向(可省略,默认302)
header("Location: login.php", true, 302);
exit; // 必须终止后续代码执行
}示例2:永久重定向(301)#
// 旧URL永久重定向到新URL(SEO友好)
header("Location: https://example.com/new-page", true, 301);
exit;最佳实践:#
- 重定向后**必须用
exit/die**终止代码,防止后续逻辑执行。 - 尽量使用绝对URL(尤其是跨域或代理环境下),避免相对路径的兼容性问题。
- 区分301(永久,SEO权重传递)和302(临时)的使用场景。
2.2 设置响应头(Content-Type、Content-Disposition等)#
(1)设置内容类型(Content-Type)#
告诉浏览器如何解析响应内容,避免MIME嗅探漏洞。
示例:返回JSON数据
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['status' => 'success', 'data' => ['id' => 123]]);示例:文件下载(Content-Disposition)
$filename = 'report.pdf';
header('Content-Type: application/pdf');
// 强制浏览器下载文件,filename指定默认保存名
header('Content-Disposition: attachment; filename="' . $filename . '"');
// 输出文件内容
readfile($filename);
exit;2.3 缓存控制(Cache-Control、Expires等)#
控制浏览器和中间代理的缓存行为,避免用户看到旧内容或敏感数据被缓存。
(1)禁止缓存(适用于动态页面、敏感数据)#
// 现代浏览器:Cache-Control
header("Cache-Control: no-store, no-cache, must-revalidate");
// 旧浏览器:Pragma
header("Pragma: no-cache");
// 过期时间设为过去(强制重新请求)
header("Expires: 0"); (2)强制缓存(适用于静态资源)#
// 缓存1小时(3600秒)
$expires = 3600;
header("Cache-Control: public, max-age={$expires}");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expires) . " GMT");2.4 HTTP基本认证#
要求用户输入用户名密码才能访问页面,常用于后台管理系统。
// 检查是否提供了认证信息
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
// 要求浏览器弹出认证窗口
header('WWW-Authenticate: Basic realm="Admin Area"');
// 认证失败,返回401状态码
header('HTTP/1.0 401 Unauthorized');
echo '请输入正确的用户名和密码';
exit;
} else {
// 验证用户名密码(示例:硬编码,实际需查数据库)
$validUser = 'admin';
$validPass = '123456';
if ($_SERVER['PHP_AUTH_USER'] === $validUser && $_SERVER['PHP_AUTH_PW'] === $validPass) {
echo '欢迎,管理员!';
} else {
// 认证失败,重新要求认证
header('WWW-Authenticate: Basic realm="Admin Area"');
header('HTTP/1.0 401 Unauthorized');
echo '用户名或密码错误';
exit;
}
}2.5 自定义响应状态码#
显式设置HTTP状态码,告知客户端请求的结果(成功、失败、重定向等)。
(1)返回404(页面不存在)#
// 方式1:直接发送状态码头
header("HTTP/1.1 404 Not Found");
// 方式2:通过参数显式设置
header("Content-Type: text/html", true, 404);
echo '<h1>页面不存在</h1>';(2)返回500(服务器错误)#
header("HTTP/1.1 500 Internal Server Error");
echo '服务器内部错误,请稍后重试';3. 最佳实践#
3.1 输出前调用header()#
必须确保header()在任何输出之前调用,包括:
- 文件开头的空格、BOM头(UTF-8文件若包含BOM,会导致输出提前)。
- 包含文件中的
echo或print语句。 - 模板文件中的HTML标签。
解决方案:使用输出缓冲(Output Buffering)
ob_start(); // 开启输出缓冲,暂存所有输出
echo '这部分输出会被缓冲,不影响header()';
header('Location: https://example.com'); // 仍可正常发送头
ob_end_flush(); // 输出缓冲内容(响应体)3.2 错误处理:检查headers_sent()#
若不确定是否已有输出,可通过headers_sent()检查:
if (!headers_sent()) {
header('Location: https://example.com');
exit;
} else {
echo '无法重定向,页面已输出内容';
}3.3 Location头的安全处理#
(1)使用绝对URL#
避免相对路径在代理或框架路由中出错:
// 推荐
header('Location: https://example.com/user/123');
// 不推荐(可能因环境不同导致跳转错误)
header('Location: /user/123'); (2)防止“开放重定向”漏洞#
若重定向目标由用户输入(如$_GET['redirect']),需验证目标是否在信任列表中:
$allowedDomains = ['example.com', 'api.example.com'];
$target = $_GET['redirect'] ?? 'https://example.com';
$host = parse_url($target, PHP_URL_HOST);
if (!in_array($host, $allowedDomains)) {
$target = 'https://example.com'; // 回退到安全URL
}
header('Location: ' . $target);
exit;3.4 内容安全与HTTP头#
(1)Content-Type安全#
显式声明内容类型,避免浏览器“MIME嗅探”导致XSS:
// 正确:明确JSON类型
header('Content-Type: application/json; charset=utf-8');
// 错误:依赖浏览器自动识别(可能被嗅探为HTML)
echo '{"status":"success"}'; (2)HTTP认证的安全#
HTTP基本认证的密码是明文传输的,因此必须配合HTTPS使用:
if ($_SERVER['HTTPS'] !== 'on') {
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
exit;
}
// 再执行HTTP认证逻辑...4. 常见问题及解决方案#
4.1 “Headers already sent”错误#
原因:#
- 文件开头/结尾有空格、换行或BOM头。
- 包含文件中有
echo或print。 - 模板文件在
header()前输出了HTML。
解决方案:#
- 确保PHP文件用UTF-8无BOM编码。
- 检查
include/require的文件无输出。 - 开启输出缓冲(
ob_start())。
4.2 重定向后代码继续执行#
现象:#
调用header('Location: ...')后,后续的echo、数据库操作等仍会执行。
解决方案:#
重定向后必须调用exit或die:
header('Location: https://example.com');
exit; // 终止脚本,后续代码不再执行4.3 响应状态码不生效#
现象:#
设置了header("HTTP/1.1 404 Not Found"),但浏览器仍显示200状态。
解决方案:#
- 使用
$http_response_code参数显式设置状态码:header("Content-Type: text/html", true, 404); - 确保状态码头在所有内容输出前发送。
5. 总结#
header()函数是PHP控制HTTP响应的核心工具,通过它我们可以实现页面重定向、内容类型设置、缓存控制、HTTP认证等关键功能。正确使用需注意:
- 输出前调用:确保
header()在任何内容输出前执行,必要时用输出缓冲。 - 重定向后终止:调用
header('Location: ...')后必须exit,防止代码继续执行。 - 安全防护:避免开放重定向、显式声明内容类型、HTTPS下使用HTTP认证。
- 错误处理:通过
headers_sent()检查头是否已发送,提前规避错误。
6. 参考资料#
- PHP官方文档 - header()
- OWASP HTTP Headers Security(HTTP头安全最佳实践)
- MDN HTTP Headers(HTTP头标准参考)
- PHP输出缓冲控制(解决“Headers already sent”错误)