news 2026/10/8 10:37:25

PHP setcookie() 函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PHP setcookie() 函数

前言


setcookie()的作用是让 PHP 在响应里追加一个Set-Cookie响应头,从而把一小段数据交给浏览器保存下来,并在后续请求里自动带回来。听起来很简单,但它牵扯到 HTTP 协议本身的一条硬性限制:Cookie 属于响应头的一部分,必须在任何响应体输出之前发送。


围绕这个函数最常见的三个误解是:一,以为调用setcookie()后立刻就能从$_COOKIE里读出来(实际上是下一次请求才有);二,以为设了httponly就万无一失(它只能挡住脚本读取,挡不住篡改);三,以为 Cookie 有保密性(它明文存在用户机器上,用户想怎么改就怎么改)。


本文按 PHP 官方手册讲清两种调用签名、各参数(尤其是samesite)的含义、删除 Cookie 的正确姿势,以及把 Cookie 用在不该用的地方会带来什么后果。


一、两种签名与参数含义


第一种是传统的位置参数写法:


setcookie(string $name, string $value = "", int $expires_or_options = 0,
string $path = "", string $domain = "", bool $secure = false,
bool $httponly = false): bool

第二种自 PHP 7.3.0 起提供,把后面几个参数合并成关联数组:


setcookie(string $name, string $value = "", array $options = []): bool

手册特别注明:数组形式的签名不支持命名参数。$options允许的键有expires、path、domain、secure、httponly、samesite、partitioned(partitioned是更晚的版本才加入的)。没给出的键会沿用与位置参数相同的默认值;samesite如果省略,PHP 不会发送任何 SameSite 属性。




参数类型说明



namestringCookie 名。取回时用$_COOKIE['name']

valuestringCookie 值。会明文存在用户机器上,不要放敏感信息

expires_or_optionsintUnix 时间戳(不是日期字符串)。传0或省略表示「会话 Cookie」,浏览器关闭即失效

pathstring生效路径。设为/表示整站可见;设为/admin/表示只在/admin/及其子目录可见

domainstring生效域名。设为example.com则其所有子域都可见

securebool只在 HTTPS 连接上发送

httponlybool不允许 JavaScript 读取(document.cookie拿不到),能降低 XSS 场景下的会话窃取风险

samesitestring取值只应是None、Lax或Strict;None必须同时开启secure,否则会被浏览器丢弃



expires_or_options用时间戳而不是Wdy, DD-Mon-YYYY HH:MM:SS GMT这种日期格式,是因为时区转换由 PHP 内部完成。常见写法是time() + 60 * 60 * 24 * 30(30 天后过期)。


二、必须在任何输出之前调用


setcookie()干的事本质上是加一个响应头。按 HTTP 协议,响应头必须先于响应体发出;一旦 PHP 已经向浏览器输出了任何字节,头部就锁死了。手册的措辞是:如果有输出先于本函数存在,setcookie()会失败并返回false。


这里的「输出」比想象中宽得多,包括:



  • 任何echo、print、printf;

  • ?>关闭标签之后的空行或空格(这就是纯 PHP 文件不要写结尾?>的原因);

  • 被include进来的文件产生的输出;

  • 文件开头被编辑器加上的 UTF-8 BOM(\xEF\xBB\xBF);

  • PHP 启动阶段的错误或警告信息。


配置里开了output_buffering时,输出会先进入缓冲区而不是直接发出,看起来「晚点调用也没事」,但这只是把问题掩盖了:缓冲区一旦刷出,同样的错误会出现在另一个位置,更难排查。定位这类问题最有效的手段是headers_sent():


<?php // 适用于 PHP 8.0+
if (headers_sent($file, $line)) {
// $file 与 $line 指向「第一次输出」发生的位置
error_log("响应头已发送,位置: {$file}:{$line}");
} else {
setcookie('theme', 'dark', [
'expires' => time() + 86400,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
}

三、读取、删除与安全属性


读取只能在下一次请求里发生。$_COOKIE是 PHP 在处理请求时根据请求头里的Cookie字段一次性填充的,当前请求中新设置的 Cookie 不会自动出现在里面。本次请求内要继续用这个值,就在变量里留一份。


删除 Cookie 的做法是把过期时间设到过去,并且path与domain必须和当初设置时一致,否则浏览器会认为这是另一个 Cookie,旧的依然保留:


<?php // 适用于 PHP 8.0+
setcookie('theme', '', [
'expires' => time() - 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);

关于安全属性,要准确理解它们能防什么:httponly让 JavaScript 读不到这个 Cookie,能减少 XSS 造成的会话窃取面,但对手可以直接在浏览器里改 Cookie;secure保证只在加密连接上传输,防的是明文链路上的嗅探;samesite控制跨站请求是否带上 Cookie,是缓解 CSRF 的基础手段之一。


绝对不要把有业务含义的数据直接放在 Cookie 里。「用户 ID」「角色是 admin」「商品价格」这类字段,用户改一下 Cookie 就改了业务逻辑。正确做法是只放一个不可猜的会话标识,真实状态放在服务端;如果确实需要在客户端保存状态,就要加签名:


<?php // 适用于 PHP 8.0+
declare(strict_types=1);

function signValue(string $value, string $key): string
{
$payload = base64_encode($value); // 编码,不是加密
$mac = hash_hmac('sha256', $payload, $key);

return $payload . '.' . $mac;
}

function verifyValue(string $cookie, string $key): string|false
{
$parts = explode('.', $cookie, 2);
if (count($parts) !== 2) {
return false;
}
[$payload, $mac] = $parts;
$calc = hash_hmac('sha256', $payload, $key);

if (!hash_equals($mac, $calc)) {
return false;
}
$decoded = base64_decode($payload, true);

return $decoded === false ? false : $decoded;
}

$key = 'server-side-secret-key'; // 真实项目里来自配置,不要硬编码在仓库中
$value = signValue('theme=dark', $key);

setcookie('pref', $value, [
'expires' => time() + 86400,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);

注意这里用base64_encode()只是为了把任意字节塞进 Cookie 值里不产生非法字符,它提供的是编码,不是保密性;真正防篡改的是hash_hmac()加hash_equals()的常量时间比较。


常见坑点



  1. ❌ 设完 Cookie 立刻读$_COOKIE['theme']—— 当前请求里它还不存在,只有浏览器的下一次请求才会把它带上来。✅ 本次请求内用变量保存要用的值;$_COOKIE只用来读「上一次带来的」数据。



  1. ❌ 在setcookie()前面有echo、有?>之后的空行、或文件带 UTF-8 BOM,导致Warning: Cannot modify header information - headers already sent。✅ 纯 PHP 文件不写结尾?>,编辑器保存为「无 BOM 的 UTF-8」,并把业务逻辑放在输出之前;用headers_sent($file, $line)打印出「第一次输出」的位置来定位。



  1. ❌ 删除 Cookie 时只写setcookie('theme', '')—— 没有过期时间,浏览器把它当成一个「值变为空」的会话 Cookie 继续保存并发送。✅ 传过去的expires(例如time() - 3600),且path、domain与设置时完全一致。



  1. ❌ 用 Cookie 保存用户 ID、角色、余额等有业务含义的字段 —— 全部可被用户随意修改。✅ 只存不透明的会话标识,状态放服务端;必须放客户端时用 HMAC 签名并在服务端校验。



  1. ❌ 不设置samesite—— 省略时 PHP 不会输出这个属性,跨站请求会带上 Cookie,CSRF 面变大。✅ 显式写'samesite' => 'Lax'(或'Strict');确实需要'None'时必须同时'secure' => true,否则浏览器会直接拒绝这个 Cookie。



  1. ❌ 以为写入的值会原样存进浏览器 —— 值部分会被自动 URL 编码,读回来时由 PHP 自动解码赋给$_COOKIE,前后看到的形态可能不同。✅ 需要完全不做编码地发送,用setrawcookie();但让 PHP 编码通常才是更稳的选择。



  1. ❌ 把 Cookie 当本地存储塞进大量数据 —— 浏览器对单个 Cookie 大小和单域名 Cookie 总量都有限制,且每个请求都会带上,直接反映在带宽上。✅ Cookie 里只放会话标识,其余数据留在服务端。



  1. ❌ 用isset($_COOKIE['x'])判断「用户是否登出」 ——setcookie('x', '')之后$_COOKIE['x']是空字符串,isset()依然为真,判断方向正好反了。✅array_key_exists('x', $_COOKIE) && $_COOKIE['x'] !== '',并把「是否登录」这个判断放在服务端会话里,而不是靠 Cookie 是否存在。


总结




要点结论



两种签名位置参数;PHP 7.3+ 另有$options数组形式(不支持命名参数)

调用时机必须在任何输出之前,否则失败并返回false

读取时机$_COOKIE只包含本次请求带来的 Cookie,新设的要下次请求才读得到

删除方法过期时间设为过去,且path/domain与设置时一致

安全属性secure防明文链路,httponly防脚本读取,samesite缓解 CSRF

存什么只存不透明标识;有业务含义的数据必须服务端校验或 HMAC 签名

不存什么密码、令牌明文、大量数据、任何可被篡改后直接影响授权的字段



一句话总结:setcookie()本质是「往响应头里加一行」,所以它受制于输出顺序;而 Cookie 本身是用户可读可改的客户端存储,把「不信任 Cookie 内容」和「关键状态放服务端」这两条守住,剩下的都只是属性配置问题。





版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 10:36:33

30分钟零代码搭建AI工作流:DeepSeek Harness插件实战指南

1. 为什么我花30分钟搭了这条AI工作流 先说结论&#xff1a;DeepSeek Harness v0.2 这个桌面端&#xff0c;我前后折腾了大概半小时&#xff0c;从下载安装到跑通一条能自动抓网页、整理成 Markdown、再归档到本地目录的工作流。整个过程没有写一行代码&#xff0c;全靠插件拼装…

作者头像 李华
网站建设 2026/10/8 10:36:08

研发总监AI实战:DFMEA、DOE与六西格玛的五大应用场景

1. 研发总监的AI能力补位逻辑 1.1 为什么研发总监需要亲自下场用AI 我带研发团队快八年了&#xff0c;从消费电子到工业设备都趟过。说实话&#xff0c;研发总监这个位置最尴尬的地方在于&#xff1a;你离一线技术越来越远&#xff0c;但离业务决策越来越近。图纸评审你要签字…

作者头像 李华
网站建设 2026/10/8 10:33:02

Deep Attention SMOTE:工业时序异常检测中的可学习数据增强方法

工业设备预测性维护里最让人头疼的场景&#xff0c;不是模型不够深&#xff0c;而是异常样本少得可怜。一台风机跑三个月&#xff0c;轴承故障可能只出现两次&#xff0c;每次持续几分钟&#xff0c;切出来的窗口样本撑死几十条&#xff0c;而正常工况的样本轻松上万。这种极端…

作者头像 李华
网站建设 2026/10/8 10:32:35

SmarTest 8 中 Data Logging 机制与 STDF 解析实践

做芯片测试这行&#xff0c;只要你跟过产线、调过测试程序&#xff0c;就一定绕不开 Data Logging。SmarTest 8 作为 V93000 测试机的上层软件平台&#xff0c;最让新人头疼的往往不是测试项本身怎么写&#xff0c;而是“测试跑完了&#xff0c;数据到底去哪了、格式对不对、能…

作者头像 李华
网站建设 2026/10/8 10:32:12

SpringBoot+Vue个人运动健康管理系统:Java毕设全栈项目实战拆解

做毕设最怕的就是选题看着很“高端”&#xff0c;结果自己根本驾驭不了&#xff0c;最后赶在答辩前疯狂降级。如果你正在Java方向找项目&#xff0c;今天拆的这套个人运动健康管理系统&#xff0c;是我认为很值得参考的样本&#xff1a;技术栈是SpringBoot Vue MySQL&#xf…

作者头像 李华