PHP 頁面編碼聲明關係到瀏覽器正確解碼顯示,亂碼問題 90% 源於編碼聲明缺失、錯誤或衝突。本文對比 meta 標簽與 header() 兩種方式的優先級、生效時機、最佳實踐,並給出統一規範方案。
header() | <meta charset> |
|------|---------------------|----------------------------------|
| **生效層級** | HTTP 響應頭 | HTML 文檔內部 |
| **優先級** | **最高** (瀏覽器優先遵循) | 次之 (無 HTTP 頭時使用) |
| **生效時機** | 輸出任何內容前 | 瀏覽器解析到該標簽時 |
| **適用範圍** | 整個 HTTP 響應 | 僅當前 HTML 文檔 |
| **動態控制** | ✅ PHP 邏輯控制 | ❌ 靜態寫死或模板變量 |
| **非 HTML 響應** | ✅ JSON、XML、CSV 等 | ❌ 無效 |
<?php
// 必須在任何輸出前調用 (包括空白字符、BOM)
header('Content-Type: text/html; charset=utf-8');
// 或分開設置 (PHP 5.6+)
header('Content-Type: text/html');
header('Charset: utf-8');
// JSON API 專用
header('Content-Type: application/json; charset=utf-8');
// 文件下載
header('Content-Type: application/octet-stream; charset=utf-8');
header('Content-Disposition: attachment; filename="export.csv"');
之後立即調用include/require 後調用 (若被包含文件有輸出)<!-- HTML5 簡寫 (推薦) -->
<meta charset="utf-8">
<!-- HTML4/XHTML 兼容寫法 -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!-- 必須放在 head 內,且盡可能靠前 -->
<head>
<meta charset="utf-8">
<title>...</title>
...
</head>
<title>) 可能已按錯誤編碼解析<?php
// 測試文件:test.php
header('Content-Type: text/html; charset=gbk'); // HTTP 頭聲明 GBK
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"> <!-- meta 聲明 UTF-8 -->
</head>
<body>
中文測試:如果顯示亂碼說明 HTTP 頭生效 (GBK 解碼 UTF-8 字節)
</body>
</html>
結果: 瀏覽器按 GBK 解碼**,頁面顯示亂碼。證實 HTTP 頭優先級高於 meta。
<?php
// public/index.php 或 bootstrap/app.php 最頂部
// 1. 設置默認編碼 (PHP 5.6+ 影響 default_charset)
ini_set('default_charset', 'UTF-8');
// 2. 統一發送 HTTP 頭 (封裝為中間件/基類)
function sendUtf8Header(): void
{
// 避免重複發送
if (!headers_sent()) {
header('Content-Type: text/html; charset=utf-8');
}
}
// 3. 模板基類自動輸出 meta (雙重保險)
abstract class BaseView
{
protected function renderHead(): void
{
echo '';
}
}
; php.ini
default_charset = "UTF-8"
; 此設置自動為所有響應添加 charset=utf-8 (除非被 header() 覆蓋)
{-- Blade/Laravel --}
<!DOCTYPE html>
<html>
<head>
<meta charset="{{ config('app.charset', 'utf-8') }}">
@yield('head')
</head>
...
{-- Twig/Symfony --}
<!DOCTYPE html>
<html>
<head>
<meta charset="{{ charset|default('utf-8') }}">
{% block head %}{% endblock %}
</head>
...
Content-Type 2. 檢查 meta charset 3. 檢查文件保存編碼 |
| 數據庫讀出亂碼 | 連接編碼未設置 | SET NAMES utf8mb4 / PDO charset=utf8mb4 |
| 文件下載中文文件名亂碼 | RFC 5987 編碼缺失 | filename*=UTF-8'' + rawurlencode() |
| curl/file_get_contents 獲取亂碼 | 源站編碼非 UTF-8 | iconv('GBK', 'UTF-8', $content) |
| JSON 中文被轉義 \uXXXX | json_encode 默認轉義 | json_encode($data, JSON_UNESCAPED_UNICODE) |
php.ini 設置 default_charset = "UTF-8"header('Content-Type: text/html; charset=utf-8')<meta charset="utf-8">charset=utf8mb4utf8mb4_general_ci 或 utf8mb4_unicode_ciapplication/json; charset=utf-8Content-Disposition 使用 RFC 5987 編碼編碼問題本質是「發送端聲明」與「接收端解碼」不一致。建立全鏈路 UTF-8 規範,從文件保存到 HTTP 響應,亂碼問題將徹底消失。