Fix PHP Warning: session_start(): Failed to read session data: files

intermediate🐘 PHP2026-04-02| PHP 7.x / 8.x trên Linux (Ubuntu, Debian, CentOS), Apache/Nginx + PHP-FPM

Error Message

Warning: session_start(): Failed to read session data: files
#php#session#permission#tmp

Lỗi Gặp Phải

Ứng dụng của bạn đang ghi lỗi này vào log:

Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions)
Warning: session_start(): open(SESSION_PATH/sess_abc123, O_RDWR) failed: Permission denied (13)

Đôi khi đi kèm với:

Warning: session_start(): Failed to read session data: files (path: /tmp)

Người dùng bị đăng xuất ngẫu nhiên, giỏ hàng bị xóa sạch, form đăng nhập không lưu được trạng thái. Đây là lỗi session điển hình xảy ra vào thời điểm tệ nhất có thể.

Nguyên Nhân

PHP ghi dữ liệu session xuống đĩa. Tiến trình chạy PHP — thường là www-data, apache, hoặc nginx — không có quyền ghi vào đường dẫn lưu session, hoặc thư mục đó hoàn toàn không tồn tại.

Ba nguyên nhân phổ biến gây ra lỗi này:

  • Sai owner của thư mục session (phổ biến nhất)
  • Sai quyền truy cập — thư mục tồn tại nhưng user của PHP không ghi được
  • Sai đường dẫn trong php.ini trỏ đến nơi không tồn tại

Trên Ubuntu/Debian, các pool của PHP-FPM chạy dưới user www-data. Nhưng sau khi nâng cấp distro hoặc cài lại package thủ công, thư mục session thường bị đặt lại về quyền sở hữu của root — và đột nhiên mọi thứ ngừng hoạt động.

Bước 1 — Xác Định Đường Dẫn Session

Kiểm tra PHP đang thực sự lưu session vào đâu:

php -r "echo session_save_path();"

Hoặc từ một file PHP:

<?php echo ini_get('session.save_path'); ?>

Cũng kiểm tra trong php.ini:

php --ini
grep -r 'session.save_path' /etc/php/

Các đường dẫn phổ biến cần kiểm tra:

  • /var/lib/php/sessions (mặc định trên Ubuntu/Debian)
  • /tmp
  • /var/lib/php/session (CentOS/RHEL)
  • Đường dẫn tùy chỉnh được đặt trong cấu hình pool hoặc php.ini

Bước 2 — Kiểm Tra Owner và Quyền Truy Cập

ls -la /var/lib/php/sessions

Bạn có thể thấy kết quả như sau:

drwx-wx-wt 2 root root 4096 Apr  1 02:14 sessions

Kiểm tra user mà PHP-FPM đang chạy:

ps aux | grep php-fpm | grep -v grep

Hoặc kiểm tra trực tiếp trong cấu hình pool:

grep -E '^user|^group' /etc/php/8.*/fpm/pool.d/www.conf

Cách Sửa 1 — Sửa Owner (Phổ Biến Nhất)

Nếu PHP-FPM chạy với user www-data:

sudo chown root:www-data /var/lib/php/sessions
sudo chmod 770 /var/lib/php/sessions

Trên CentOS/RHEL khi PHP-FPM chạy với user apache:

sudo chown root:apache /var/lib/php/sessions
sudo chmod 770 /var/lib/php/sessions

Ubuntu đặt sticky bit (+t) theo mặc định — hãy giữ nguyên. Nó ngăn một user xóa file session của user khác:

sudo chmod 1770 /var/lib/php/sessions

Cách Sửa 2 — Thư Mục Không Tồn Tại

Các bản cập nhật package đôi khi xóa hoàn toàn thư mục này. Hãy tạo lại:

sudo mkdir -p /var/lib/php/sessions
sudo chown root:www-data /var/lib/php/sessions
sudo chmod 1770 /var/lib/php/sessions

Cách Sửa 3 — Dùng Đường Dẫn Session Riêng Cho Từng Ứng Dụng

Không thể chỉnh sửa thư mục hệ thống? Đang chạy nhiều ứng dụng dưới các user khác nhau? Hãy đặt đường dẫn session tùy chỉnh cho từng ứng dụng:

<?php
$sessionDir = __DIR__ . '/../storage/sessions';
if (!is_dir($sessionDir)) {
    mkdir($sessionDir, 0700, true);
}
session_save_path($sessionDir);
session_start();

Đặt thư mục này ngoài web root — không được truy cập được qua HTTP. Sau đó xác nhận quyền ghi:

ls -la storage/sessions/

Cách Sửa 4 — Cấu Hình Ở Cấp Pool PHP-FPM

Thay vì chỉnh php.ini toàn cục, hãy đặt đường dẫn session trong cấu hình pool. Cách này giúp cô lập session theo từng virtual host:

# /etc/php/8.2/fpm/pool.d/myapp.conf
[myapp]
user = www-data
group = www-data
php_value[session.save_path] = /var/lib/php/sessions/myapp

Sau đó tạo thư mục và cấp quyền:

sudo mkdir -p /var/lib/php/sessions/myapp
sudo chown www-data:www-data /var/lib/php/sessions/myapp
sudo chmod 700 /var/lib/php/sessions/myapp
sudo systemctl restart php8.2-fpm

Cách Sửa 5 — SELinux Chặn Quyền Ghi (CentOS/RHEL)

Quyền truy cập trông có vẻ đúng nhưng session vẫn lỗi trên CentOS/RHEL? SELinux có thể đang âm thầm chặn các thao tác ghi. Kiểm tra audit log:

sudo ausearch -m avc -ts recent | grep php

Nếu thấy các lần từ chối, hãy khôi phục ngữ cảnh SELinux đúng:

sudo restorecon -R /var/lib/php/sessions

Hoặc chuyển tạm sang chế độ permissive để xác nhận SELinux là thủ phạm:

sudo setenforce 0
# kiểm tra session
sudo setenforce 1

Xác Nhận Đã Sửa Xong

Đến lúc kiểm tra xem có thực sự hoạt động không. Chạy script test này ngay sau khi áp dụng cách sửa:

<?php
session_start();
$_SESSION['test'] = 'ok';
echo 'Session ID: ' . session_id() . PHP_EOL;
echo 'Save path: ' . session_save_path() . PHP_EOL;
echo 'Status: ' . (session_status() === PHP_SESSION_ACTIVE ? 'ACTIVE' : 'FAILED');

Sau đó kiểm tra xem file session có thực sự được tạo không:

ls -la /var/lib/php/sessions/

Bạn sẽ thấy các file có dạng sess_abc123xyz với timestamp gần đây. Nếu thư mục trống sau khi chạy test, nghĩa là việc ghi vẫn đang thất bại.

Theo dõi PHP error log theo thời gian thực trong khi test:

sudo tail -f /var/log/php8.2-fpm.log
# hoặc
sudo journalctl -u php8.2-fpm -f

Mẹo Hữu Ích

Không chắc chmod 1770 hay 770 thực sự cấp quyền gì lúc 2 giờ sáng? Unix Permissions Calculator trên ToolCraft cho phép bạn trực quan hóa các bit phân quyền ngay lập tức — trước khi vô tình mở quá rộng hoặc khóa PHP ra ngoài hoàn toàn.

Phòng Ngừa

  • Thêm healthcheck: Một cron job chạy mỗi 5 phút để kiểm tra thư mục session có tồn tại và ghi được không — và gửi cảnh báo — sẽ phát hiện vấn đề trước khi người dùng gặp phải.
  • Cố định đường dẫn trong php.ini: Đừng dựa vào giá trị mặc định. Đặt tường minh session.save_path để nó không bao giờ âm thầm thay đổi sau khi nâng cấp package.
  • Dùng Redis hoặc Memcached cho session trên các site có hàng trăm người dùng đồng thời: session.save_handler = redis với session.save_path = "tcp://127.0.0.1:6379" loại bỏ hoàn toàn các vấn đề phân quyền filesystem.
  • Giám sát lỗi session: Thiết lập cảnh báo log cho chuỗi session_start(): Failed để phát hiện sự cố trước khi người dùng báo cáo.
  • Sau khi nâng cấp OS: Luôn chạy ls -la /var/lib/php/sessions — các bản cập nhật package đôi khi đặt lại owner về root.

Related Error Notes