某日專案中發現:提交包含 2000+ 輸入框的大型表單時,後端 $_POST 只收到前 1000 個字段,剩余數據神秘「丟失」。經排查,罪魁禍首是 PHP 5.3.9 引入的 max_input_vars 配置項,默認值恰為 1000。本文深度解析其機制、影響範圍、排查步驟與調優建議。
<form method="post">
<?php for ($i = 0; $i < 2000; $i++): ?>
<input type="text" name="field[<?=$i?>]" value="<?=$i?>">
<?php endfor; ?>
<button type="submit">提交</button>
</form>
提交後 print_r($_POST['field']) 只輸出 0~999,1000~1999 全無。
max_input_vars 限制**單次請求**中可接收的**輸入變量總數**(包括 $_GET、$_POST、$_COOKIE 三者之和)。超出部分**靜默丟棄**,不報錯、不拋異常,僅在 error_log 記錄一行警告:
PHP Warning: Unknown: Input variables exceeded 1000. To increase the limit change max_input_vars in php.ini.
該限制旨在防止 HashDoS 攻擊(惡意構造大量同哈希鍵導致 CPU 100%)。
| 超全局變量 | 是否受限 | 備註 |
|---|---|---|
$_GET | ✅ | URL 查詢字串參數數量 |
$_POST | ✅ | 表單字段、JSON 解析後的鍵數 |
$_COOKIE | ✅ | Cookie 鍵值對數量 |
$_FILES | ✅ | 每個文件對應多個鍵(name、type、tmp_name...) |
$_REQUEST | ✅ | 合併後總數受限 |
php://input | ❌ | 原始 Body 不受影響,可自行解析繞過 |
tail -f /var/log/php-fpm/error.log 搜尋 Input variables exceeded。php -i | grep max_input_vars 或 ini_get('max_input_vars')。count($_POST, COUNT_RECURSIVE) + count($_GET) + count($_COOKIE)。field[0][sub]=1 算 1 個變量(field),但 count(..., COUNT_RECURSIVE) 會遞歸計數葉子節點,**實際限制的是哈希表鍵數**,即陣列每一層的鍵名總和。; php.ini
max_input_vars = 5000 ; 根據業務峰值預估,建議留 2-3 倍餘量
修改後**必須重啟 PHP-FPM**:
systemctl reload php-fpm
# 或
service php-fpm reload
# Docker
docker exec -it php-fpm kill -USR2 1
; 放在項目公共入口目錄
max_input_vars = 5000
需 user_ini.filename = ".user.ini" 且 user_ini.cache_ttl 過期後生效(默認 300s)。
可在 Web 服務器層限制請求體大小(client_max_body_size),但無法精確控制變量數,治標不治本。
若表單極大(如 10萬+ 字段),調大 max_input_vars 會增加哈希表攻擊面。最佳實踐:
// 前端:收集表單數據打包為 JSON
const data = {};
document.querySelectorAll('input[name^="field"]').forEach(el => data[el.name] = el.value);
fetch('/api/submit', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
// 後端:讀取原始流手動解析
$json = file_get_contents('php://input');
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
// $data 現在包含所有字段,不受 max_input_vars 限制
優勢:繞過 PHP 內建解析器限制、減少哈希表攻擊面、支援更複雜的巢狀結構。
| 方案 | 安全性 | 適用場景 |
|---|---|---|
| 調大 max_input_vars 到 5000-10000 | 中(仍有 HashDoS 風險,需配合 Suhosin/限流) | 中型表單、快速上線 |
| JSON + php://input | 高(應用層控制解析深度/大小) | 大型表單、API 接口、長期維護專案 |
| 分批提交(分頁/分步) | 最高(單次變量極少) | 極複雜業務、用戶體驗允許分步 |
max_input_vars=1000 是 PHP 防禦 HashDoS 的重要防線,但也成為大型表單的隱形天花板。排查口訣:日誌有警告 → 變量數超限 → 調大配置或改 JSON 提交。生產環境建議:常規表單調大至 3000-5000 並配合 WAF 限流;核心大數據接口徹底遷移至 JSON Body,從根源消除限制與風險。