Lỗi 403 Forbidden xuất hiện khi Nginx nhận được request nhưng từ chối phục vụ tài nguyên. Nguyên nhân có thể nằm ở cấu hình location, thiếu file index, quyền đọc/traverse trên đường dẫn, user chạy worker hoặc chính sách SELinux. Vì vậy, sửa bằng chmod -R 777 thường vừa không giải quyết đúng gốc rễ vừa làm yếu bảo mật.
Câu trả lời ngắn: hãy kiểm tra theo thứ tự: server block và root, file index, error log, quyền execute trên từng thư mục, quyền đọc file, user Nginx, rồi SELinux context/denial nếu hệ thống đang enforcing.403 Forbidden khác gì 404 và 502?
403 có nghĩa request đã tới đúng server hoặc location nhưng bị từ chối. 404 thường cho biết tài nguyên không tồn tại hoặc không được mapping đúng. 502 lại liên quan upstream như PHP-FPM hoặc proxy backend. Phân biệt status giúp khoanh vùng: đừng tìm socket PHP khi request đang bị chặn trước bước chuyển vào upstream.
Bước 1: xác nhận server block và root
Trước tiên, kiểm tra Nginx đang nạp cấu hình nào. Dùng nginx -T để xem toàn bộ config đã merge, sau đó tìm đúng server_name, listen, root, alias và các rule deny.
sudo nginx -T 2>/dev/null | less
sudo nginx -T 2>/dev/null | grep -nE 'server_name|root |alias |deny |allow |index |try_files'
sudo nginx -tDirective root nối URI với đường dẫn file theo quy tắc của Nginx. Với alias, phần location được thay bằng đường dẫn alias, nên chỉ một dấu slash hoặc pattern sai cũng có thể khiến request trỏ sang thư mục không như dự kiến. Nếu vừa đổi root, hãy dùng curl -I và log để xác nhận request thực sự vào server block nào.
Bước 2: kiểm tra file index và directory listing
Nginx xử lý request kết thúc bằng slash qua module index. Nếu thư mục không có file index phù hợp, request có thể rơi vào directory listing; module autoindex mặc định tắt. Khi đó, truy cập /uploads/ có thể trả 403 dù file riêng lẻ bên trong vẫn đọc được.
server {
root /var/www/example/current/public;
index index.html index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Không bật directory listing nếu không có yêu cầu rõ ràng
autoindex off;
}Không nên bật autoindex on chỉ để “hết 403” trên thư mục chứa backup, file upload riêng tư hoặc artifact deploy. Nếu mục tiêu là phục vụ một file tĩnh, hãy truy cập đúng URL file hoặc tạo index an toàn có chủ đích.
Bước 3: đọc error log thay vì đoán
Error log thường chỉ ra nguyên nhân cụ thể như permission denied, directory index of ... is forbidden, rule access bị từ chối hoặc symlink không được phép. Đường dẫn log phụ thuộc distro và cấu hình, vì vậy hãy lấy từ nginx -T hoặc file site hiện hành.
sudo tail -n 100 /var/log/nginx/error.log
sudo journalctl -u nginx --since '15 minutes ago' --no-pager
sudo grep -R "permission denied\|directory index\|forbidden" /var/log/nginx 2>/dev/nullTrong lúc tái hiện lỗi, mở một terminal theo dõi log rồi dùng curl -i https://example.com/path. Cách này giúp nối chính xác request với dòng log thay vì đọc một log cũ không liên quan.
Bước 4: kiểm tra quyền traverse của từng thư mục
Để đọc /var/www/example/current/public/index.html, user Nginx không chỉ cần quyền đọc file. Process còn cần quyền x trên từng thư mục cha để đi xuyên qua path. Dùng namei hoặc stat để kiểm tra từng tầng.
namei -l /var/www/example/current/public/index.html
stat -c '%A %a %U:%G %n' \
/var/www/example \
/var/www/example/current \
/var/www/example/current/public \
/var/www/example/current/public/index.htmlMột mô hình an toàn phổ biến là thư mục public cho phép owner/group đọc và traverse, file tĩnh có quyền đọc, còn thư mục chứa secret hoặc release metadata không nằm dưới document root. Đừng thay toàn bộ cây bằng 777; hãy xác định chính xác tầng đang thiếu quyền.
Quyền file và user chạy Nginx
User worker phụ thuộc distro và config, thường là www-data trên Ubuntu/Debian hoặc nginx trên một số hệ thống khác. Kiểm tra process thực tế thay vì đoán.
ps -eo user,group,pid,comm,args | grep '[n]ginx'
grep -R '^user ' /etc/nginx/nginx.conf /etc/nginx/conf.d 2>/dev/null
# Kiểm tra quyền đọc bằng user worker sau khi xác định đúng tên
sudo -u www-data test -r /var/www/example/current/public/index.html && echo readable
sudo -u www-data namei -l /var/www/example/current/public/index.htmlNếu deploy bằng user khác, đừng mặc định rằng đổi owner toàn bộ project sang user Nginx là lựa chọn tốt. Web process chỉ cần đọc public và ghi vào các thư mục upload/cache đã thiết kế riêng. Tách quyền đọc và quyền ghi giúp giảm tác động nếu ứng dụng bị khai thác.
Symlink và release directory
Nhiều hệ thống deploy dùng symlink như current trỏ tới release. Khi đó cần kiểm tra cả target và chính sách disable_symlinks. Một symlink có thể trỏ ra ngoài document root, trỏ tới release đã bị xóa hoặc đi qua thư mục mà worker không có quyền traverse.
readlink -f /var/www/example/current
namei -l /var/www/example/current/public/index.html
find -L /var/www/example/current/public -maxdepth 1 -type l -lsKhông bật hoặc tắt disable_symlinks một cách máy móc. Hãy đọc directive đang áp dụng, kiểm tra design deploy và chỉ cho phép symlink khi đường dẫn target được kiểm soát.
SELinux: kiểm tra context trước khi đổi mode
Trên hệ thống có SELinux enforcing, permission Unix đúng vẫn chưa đủ. SELinux là một lớp Mandatory Access Control; policy quyết định process nào được phép tương tác với file. Đây là lý do một lệnh chmod hợp lệ vẫn có thể không giải quyết 403.
getenforce
ls -Z /var/www/example/current/public/index.html
sudo ausearch -m AVC,USER_AVC -ts recent | audit2why
sudo journalctl -t setroubleshoot --since '15 minutes ago'Nếu file nằm ở thư mục không có context web phù hợp, hãy xác định type đúng theo tài liệu distro và dùng semanage fcontext kết hợp restorecon để giữ thay đổi bền vững. Không nên tắt SELinux để “test nhanh” rồi quên bật lại trong production.
# Ví dụ minh họa; kiểm tra type phù hợp với hệ thống trước khi áp dụng
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/example/public(/.*)?'
sudo restorecon -Rv /srv/example/publicTrên Ubuntu, AppArmor có thể là lớp bảo vệ liên quan thay vì SELinux. Hãy xác định cơ chế MAC đang hoạt động trên distro trước khi dùng lệnh của hệ khác.
Các lỗi cấu hình thường gây 403
Một số pattern hay gặp gồm deny all đặt ở location cha, allow/deny không đúng CIDR, try_files trỏ vào thư mục không có index, regex location bắt nhầm file, alias nối path sai và rule bảo vệ file dot hoặc backup. Tìm tất cả rule access bằng nginx -T, không chỉ mở file config bạn vừa sửa.
# Ví dụ cần review cẩn thận
location /private/ {
deny all;
}
location ~ /\. {
deny all;
}
location /assets/ {
alias /srv/example/assets/;
}
Rule chặn file bắt đầu bằng dấu chấm có thể đúng với .env, nhưng regex quá rộng cũng có thể chặn path hợp lệ. Sau mỗi thay đổi, chạy nginx -t, reload có kiểm soát và kiểm tra một URL đại diện.
Quy trình sửa và rollback an toàn
Trước khi sửa, sao lưu file config và ghi nhận trạng thái hiện tại. Thay đổi nhỏ nhất có thể, kiểm tra cú pháp, reload rồi test bằng curl. Nếu status vẫn sai hoặc xuất hiện lỗi mới, quay lại bản config trước thay vì tiếp tục cộng thêm chmod và rule ngoại lệ.
sudo cp -a /etc/nginx/sites-available/example \
/etc/nginx/sites-available/example.before-403-fix
sudo nginx -t && sudo systemctl reload nginx
curl -i https://example.com/path
# Rollback khi cần
sudo cp -a /etc/nginx/sites-available/example.before-403-fix \
/etc/nginx/sites-available/example
sudo nginx -t && sudo systemctl reload nginxChecklist chẩn đoán nhanh
Xác nhận request vào đúng server block và location.Kiểm tra root/alias, index và try_files.Đọc error log ngay lúc tái hiện lỗi.Dùng namei để kiểm tra quyền traverse từng thư mục.Kiểm tra quyền đọc file và user worker thực tế.Kiểm tra symlink, release target và disable_symlinks.Nếu có SELinux/AppArmor, đọc denial/context thay vì tắt cơ chế bảo vệ.Không dùng chmod 777 như giải pháp mặc định.Test bằng curl và chuẩn bị rollback.
Kết luận
Nginx 403 Forbidden là một triệu chứng, không phải một nguyên nhân duy nhất. Cách xử lý bền vững là đi từ cấu hình request đến filesystem và lớp bảo mật bắt buộc, ghi nhận bằng log rồi sửa đúng điểm. Khi đã tách document root, quyền đọc và quyền ghi hợp lý, các lỗi 403 sẽ dễ chẩn đoán hơn mà không phải đánh đổi an toàn của VPS.
Bài này bổ sung cho hướng dẫn cấu hình Nginx reverse proxy nhiều website và bài xử lý Nginx 502 Bad Gateway. Nguồn kỹ thuật chính gồm tài liệu NGINX core module, NGINX index module, NGINX autoindex module và Red Hat SELinux guide.