document.cookie 设置有效期:max-age vs expires 与删除的坑

document.cookie 是一个字符串 API,“设置有效期”其实就是在 cookie 字符串里追加 expiresmax-age 属性。

规则很简单:不写有效期 = 会话 cookie(浏览器关掉就没)写了 = 持久化 cookie

方式一:expires(老写法)

传一个 UTC 时间字符串:

const d = new Date();
d.setTime(d.getTime() + 7 * 24 * 60 * 60 * 1000);   // 7 天

document.cookie = `token=abc; expires=${d.toUTCString()}; path=/`;

注意必须是 toUTCString(),用 toString()toISOString() 会挂。

方式二:max-age(现代推荐)

直接指定”多少秒后过期”:

document.cookie = `token=abc; max-age=${7 * 24 * 60 * 60}; path=/`;

单位是秒。max-age 更直观,也不用管时区。老到 IE 那一档才要考虑 expires

本质就是”立刻过期”:

document.cookie = "token=; max-age=0; path=/";
// 或
document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";

三个删不掉的坑

1. path 不一致

创建时用 path=/,删除也必须带上 path=/。不带就等于删的是当前路径下的 cookie,很可能和实际存放的 cookie 不是同一个。

2. domain 不一致

.example.comsub.example.com 是两条不同的 cookie。删除时必须匹配写入时的 domain。

服务端设置了 HttpOnly 的 cookie,document.cookie 完全读不到、也删不掉。只能后端接口去清。

封装模板

前端项目里放一份省心:

export function setCookie(name, value, days = 7, options = {}) {
  const d = new Date();
  d.setTime(d.getTime() + days * 864e5);          // 864e5 = 24*60*60*1000

  const parts = [
    `${name}=${encodeURIComponent(value)}`,
    `expires=${d.toUTCString()}`,
    `path=${options.path || "/"}`,
  ];
  if (options.domain)   parts.push(`domain=${options.domain}`);
  if (options.secure)   parts.push("secure");
  if (options.sameSite) parts.push(`samesite=${options.sameSite}`);

  document.cookie = parts.join("; ");
}

export function getCookie(name) {
  return document.cookie
    .split("; ")
    .find(row => row.startsWith(name + "="))
    ?.split("=")[1];
}

export function deleteCookie(name, options = {}) {
  setCookie(name, "", -1, options);   // 复用同样的 path/domain
}

上生产别忘的属性

正经站点几乎必配的三个属性:

  • Secure:只走 HTTPS
  • SameSite=Lax(登录态用 Strict 更安全)
  • HttpOnly(防 XSS 盗 token,只能后端 Set-Cookie 时写)

前端能设的就 SecureSameSiteHttpOnly 必须走服务端。

一句话总结

现代写法用 max-age、删除记得带 path=/、跨域跨子域记得对齐 domain。真安全靠 HttpOnly + Secure + SameSite,那部分归后端管。