Site logo

Upload File Lớn Trong PHP/Laravel: Fix Lỗi 413 Và Out Of Memory

5:00 read

Bài này kể lại một case thực tế: QC upload một file CSV 9.99MB vào màn hình import, lần đầu nhận lỗi 413 The POST data is too large., sửa xong thì chuyển sang lỗi 500 với response trống. Hai lỗi nhìn đơn giản, nhưng nguyên nhân nằm ở bốn tầng khác nhau: nginx, PHP-FPM, Laravel, và chính cách code đọc file.

Đi qua từng bước debug trong case này, bạn sẽ nắm được:

  • Một request upload đi qua những "cửa" giới hạn nào, và cửa nào trả lỗi gì.
  • Cách chỉnh upload_max_filesize, post_max_size, memory_limit đúng chỗ (và cái bẫy của .user.ini).
  • Vì sao tăng RAM không phải là cách sửa, và cách đo bộ nhớ thật của một đoạn code PHP.
  • Cách refactor code đọc CSV sang streaming bằng generator: RAM của reader giảm từ ~930MB xuống 16MB.

Tổng Quan: Request Upload Đi Qua Những Tầng Nào?

Trước khi debug, cần có bức tranh toàn cảnh. Với stack phổ biến nginx → PHP-FPM → Laravel, một request multipart/form-data phải đi qua lần lượt các giới hạn sau:

Tầng Cấu hình Giá trị mặc định Khi vượt giới hạn
nginx client_max_body_size 1m nginx trả 413 (HTML của nginx)
PHP-FPM post_max_size 8M PHP bỏ trống $_POST/$_FILES, Laravel trả 413 "The POST data is too large."
PHP-FPM upload_max_filesize 2M File bị đánh dấu lỗi UPLOAD_ERR_INI_SIZE, Laravel validation fail (422)
Laravel Rule max:10240 (đơn vị KB) Tùy code 422 kèm message validation
PHP runtime memory_limit 128M Fatal error, response thường là 500 trống
OS RAM + swap Tùy server OOM killer kill process, 502

Điểm quan trọng: mỗi tầng trả một mã lỗi khác nhau. Nhìn vào mã lỗi và nội dung response là đoán được request đã "chết" ở tầng nào:

  • 413 với body JSON The POST data is too large. → PHP (post_max_size), không phải nginx.
  • 413 với trang HTML 413 Request Entity Too Large → nginx.
  • 500 với body rỗng → PHP fatal error (thường là hết memory hoặc timeout), Laravel không kịp render JSON lỗi.

Bước 1: Tái Hiện Lỗi Bằng curl Thay Vì Click Trên UI

Đừng debug bằng cách nhờ QC upload lại. Hãy tạo một file cùng kích thước và bắn thẳng vào API:

# Tạo file ngẫu nhiên ~9.99MB
head -c 10475274 /dev/urandom > f999.csv

# POST vào một route không tồn tại để chỉ test tầng giới hạn body
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
  -X POST -H 'Accept: application/json' \
  -F "file=@f999.csv" \
  https://api.example.com/api/v1/ping-upload-test

Giải thích:

  • head -c N /dev/urandom: tạo file đúng N byte, không cần nội dung hợp lệ vì ta chỉ test giới hạn kích thước.
  • Gửi vào route không tồn tại là một mẹo hay: nếu body qua được mọi giới hạn, Laravel sẽ trả 404. Nếu bị chặn ở PHP, ta nhận 413. Không cần auth, không ghi dữ liệu gì.
  • -H 'Accept: application/json' để Laravel trả JSON thay vì redirect.

Kết quả lần đầu: HTTP 413. Đã tái hiện được lỗi.

Yêu cầu đăng nhập

Vui lòng đăng nhập để truy cập nội dung này

Additional Resources

Course Guide

Comprehensive PDF guide with examples

GitHub Repository

Example code for all lessons

Discussion

Have a question about this lesson? Post it here and get answers from instructors and peers.