主域名 example.com 設定 Cookie,需讓 a.example.com、b.example.com 等所有子域名可讀取。關鍵在於 setcookie() 第 5 參數 domain 設為 .example.com (前置點號) 或 example.com。本文詳細解析三種寫法差異、瀏覽器相容性、安全屬性 (Secure、HttpOnly、SameSite)、跨域 Session 共享方案與封裝類別。
| 寫法 | Cookie Domain | 可讀取域名 | 標準合規 | 備註 |
|---|---|---|---|---|
setcookie('k','v',...,'/','*.example.com') | *.example.com | ❌ 失效 | ❌ 非標準 | 通配符星號不被支援 |
setcookie('k','v',...,'/','.example.com') | .example.com | ✅ example.com + *.example.com | ✅ RFC 2109/6265 | 推薦,前置點號明確表示包含子域 |
setcookie('k','v',...,'/','example.com') | example.com | ✅ example.com + *.example.com | ✅ RFC 6265 | 現代瀏覽器同樣支援子域,但舊版 IE 可能有問題 |
$expire,
'path' => '/', // 全站可用
'domain' => '.' . $domain, // 關鍵:前置點號共享子域
'secure' => true, // 僅 HTTPS 傳輸 (生產必開)
'httponly' => true, // JS 無法讀取 (防 XSS 竊取)
'samesite' => 'Lax', // CSRF 防護: Lax/Strict/None
]);
name = $name;
$this->expire = $expire ?: time() + 3600 * 24 * 30;
$this->path = $path;
$this->domain = $domain ? ('.' . ltrim($domain, '.')) : '';
$this->secure = $secure;
$this->httponly = $httponly;
$this->samesite = $samesite;
$this->load();
}
private function load(): void {
if (isset($_COOKIE[$this->name])) {
$val = $_COOKIE[$this->name];
if (get_magic_quotes_gpc()) $val = stripslashes($val);
$this->data = @unserialize($val) ?: [];
}
}
public function get(string $key, $default = null) {
return $this->data[$key] ?? $default;
}
public function set(string $key, $value): void {
$this->data[$key] = $value;
}
public function delete(string $key): void {
unset($this->data[$key]);
}
public function clear(): void {
$this->data = [];
}
public function save(): void {
$val = serialize($this->data);
if (strlen($val) > 4000) {
trigger_error("Cookie {$this->name} exceeds 4KB limit", E_USER_WARNING);
}
setcookie($this->name, $val, [
'expires' => $this->expire,
'path' => $this->path,
'domain' => $this->domain,
'secure' => $this->secure,
'httponly' => $this->httponly,
'samesite' => $this->samesite,
]);
}
}
// 使用
$cookie = new CookieManager('app_data', 0, '/', 'example.com');
$cookie->set('user_id', 12345);
$cookie->set('prefs', ['theme'=>'dark','lang'=>'zh_TW']);
$cookie->save();
// 讀取
$userId = $cookie->get('user_id');
$prefs = $cookie->get('prefs', []);
常見問題排查
🔧 避坑指南
- 開發環境 localhost:無法設定
.localhost。解決:修改/etc/hosts加入127.0.0.1 dev.example.com,用dev.example.com訪問。- Secure=true 但本地無 HTTPS:Cookie 不會被瀏覽器儲存。開發環境暫設
secure=false或用mkcert簽本地憑證。- SameSite=None 需 Secure=true:跨站請求需攜帶 Cookie 時設
samesite='None', secure=true。- 4KB 大小限制:單 Cookie 不超 4KB,總數每域 ~150-300 個。大量資料請用 Session/資料庫/本地儲存。
- 子域名無法讀取:檢查
domain參數是否含前置點、path是否為/、是否同一主域名 (公共後綴列表影響,如github.io不共享)。正確設定
domain=.example.com配合Secure、HttpOnly、SameSite,是實現跨子域名認證共享的基石。敏感資料 (用戶 ID、權限) 優先用 Session +session.cookie_domain,非敏感偏好設定可用 Cookie。