無數 PHP 開發者被 UTF-8 BOM (Byte Order Mark) 坑過:明明代碼沒錯,卻出現「無法登入/登出」「頁頂莫名空白」「Cannot modify header information - headers already sent」。本文徹底講透 BOM 產生原因、危害機制、檢測清除工具鏈、編輯器配置最佳實踐與 CI/CD 守門方案。
EF BB BF,用於標識字節序 (UTF-8 實際不需要,但 Windows 記事本等編輯器慣性添加)。<?php 前的「內容輸出」。| 癥狀 | 觸發條件 | 底層原因 |
|---|---|---|
| 頁頂出現不明空白/亂碼 | 包含文件、模板輸出、直接訪問 | BOM 作為 HTML 文本節點輸出,撐開 <body> 頂部 |
Cannot modify header information - headers already sent | header()、setcookie()、session_start()、重定向 | HTTP 協議要求 Header 在 Body 之前。BOM 導致 Body 過早開始,Header 發送失敗 |
| 無法登入/登出、Session 失效 | 依賴 Cookie/Session 的認證系統 | session_start() 內部調用 setcookie() 發送 Set-Cookie Header,受上條影響失敗 |
# 掃描項目所有 PHP/HTML/JS/CSS/INI 文件
grep -r -l $'^\xEF\xBB\xBF' --include="*.php" --include="*.html" --include="*.js" --include="*.css" --include="*.ini" .
# 顯示具體文件及行號 (應為第 1 行)
grep -r -n $'^\xEF\xBB\xBF' --include="*.php" .
# 批量清除 (備份後執行)
find . -type f \( -name "*.php" -o -name "*.html" -o -name "*.js" -o -name "*.css" -o -name "*.ini" \) \
-exec sed -i '1s/^\xEF\xBB\xBF//' {} +
# 驗證清除結果
grep -r $'^\xEF\xBB\xBF' --include="*.php" . && echo "仍有 BOM" || echo "已全部清除"
<?php
// scan_bom.php
$dir = $argv[1] ?? __DIR__;
$exts = ['php', 'html', 'htm', 'js', 'css', 'ini', 'xml', 'json', 'tpl', 'vue'];
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
$found = 0;
foreach ($files as $file) {
if (!$file->isFile()) continue;
$ext = strtolower($file->getExtension());
if (!in_array($ext, $exts, true)) continue;
$content = file_get_contents($file->getPathname());
if (str_starts_with($content, "\xEF\xBB\xBF")) {
echo "BOM FOUND: {$file->getPathname()}\n";
$found++;
}
}
echo "掃描完成,共發現 $found 個帶 BOM 文件。\n";
exit($found > 0 ? 1 : 0); // 非 0 退出碼,CI 可直接判斷失敗
| 編輯器/IDE | 設置路徑 | 關鍵選項 |
|---|---|---|
| VS Code | Settings (JSON) | "files.encoding": "utf8", "files.autoGuessEncoding": false (默認不寫 BOM) |
| PhpStorm / IntelliJ | Settings → Editor → File Encodings | Global/Project/Properties Encoding: UTF-8;取消勾選 "Add BOM for new files" |
| Sublime Text | Preferences → Settings | "save_with_encoding": "UTF-8" (默認無 BOM) |
| EditPlus | 工具 → 參數設置 → 文件 | 取消 "UTF-8 識別碼 (BOM)";另存為時選 "UTF-8 無 BOM" |
| Notepad++ | 設置 → 偏好設置 → 新建文檔 | 編碼選 UTF-8 (無 BOM) |
| UltraEdit | 高級 → 設置 → 文件處理 → Unicode/UTF-8 | 取消 "寫入 BOM" |
| Dreamweaver | 編輯 → 偏好設置 → 新文檔 | 取消 "包含 Unicode 簽名 (BOM)" |
| Vim/Neovim | .vimrc | set nobomb set fileencoding=utf-8 |
# .github/workflows/bom-check.yml
name: BOM Check
on: [push, pull_request]
jobs:
bom-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan BOM
run: |
php scan_bom.php .
- name: Fail if BOM found
if: failure()
run: echo "❌ 發現 BOM 文件,請清除後重新提交" && exit 1
git commit -am "backup before BOM cleanup" 或複製整個項目目錄。find + sed 或 PHP 腳本批量處理。git add -A && git commit -m "chore: remove UTF-8 BOM from all source files"。
# .gitattributes
* text=auto eol=lf
*.php text eol=lf
*.html text eol=lf
*.js text eol=lf
*.css text eol=lf
*.xml text eol=lf
*.json text eol=lf
*.md text eol=lf
# 明確聲明文本文件不應有 BOM (Git 2.19+ 支援)
*.php -bom
*.html -bom
*.js -bom
*.css -bom
UTF-8 BOM 是 Windows 記事本遺留的「禮物」,在 PHP 嵌入式輸出模型下會導致 **Header 發送失效、Session/Cookie 失效、頁面異常空白**。解決之道:編輯器配置無 BOM + CI 掃描守門 + .gitattributes 防回歸。一次徹底清理,永絕後患。請立即檢查你的項目,別讓 3 個隱形字節毀了用戶體驗。