Linux Web服务器故障排查四层诊断法
1. 这不是“重启大法”,而是故障树下的精准定位
你有没有过这样的经历:凌晨两点,手机突然弹出告警——公司官网打不开。你抓起电脑连上服务器,
systemctl status nginx
一看,服务状态是 active (running),但 curl 测试返回 502;再查 Apache,日志里满屏
Permission denied
;
df -h
显示磁盘 98% 满,可
du -sh /var/log/*
加起来才 2GB;最后发现是
/var
下某个被忽略的子目录(比如
/var/lib/php/sessions
)被日志轮转脚本漏掉了清理规则,单个 session 文件因 PHP 配置错误膨胀到 1.2GB,直接撑爆了
/var
分区——而这个分区恰好是 Apache 的 DocumentRoot 所在挂载点。整个过程耗时 47 分钟,真正修复只用了 12 秒。
这就是 Linux Web 服务器故障排查最真实的日常: 表面症状千奇百怪,根因却往往藏在系统、服务、应用三层耦合的缝隙里 。它不是靠“先重启 Nginx,不行再重启 PHP-FPM,最后 reboot”这种暴力三连就能解决的。真正的排查,是一场基于证据链的逆向工程——你得像刑侦人员一样,从一个 HTTP 500 错误码出发,一层层剥开网络栈、进程状态、文件权限、资源配额、配置语法的外壳,最终锁定那个唯一能解释所有现象的变量。
我干这行十一年,维护过从 5 台 VPS 构成的博客集群,到 300+ 节点的 SaaS 平台后端,踩过的坑足够填满三本《Linux 故障字典》。我发现一个铁律:
90% 的“疑难杂症”,其实都属于“常见问题”的变体
——只是它们披上了不同的报错外衣,混在了不同的日志位置,或者触发于特定的资源临界点。Apache 的
AH00072: make_sock: could not bind to address [::]:80
看似是端口冲突,实则可能是 systemd-socket 激活的旧实例在后台静默占着端口;Nginx 的
504 Gateway Timeout
常被归咎于 upstream 响应慢,但更大概率是
proxy_read_timeout
和
fastcgi_read_timeout
的单位被误写成毫秒而非秒;
Connection refused
不一定代表服务没启,也可能是 SELinux 的
httpd_can_network_connect
被设为 off,或者防火墙的
nftables
规则链顺序错了。
所以这篇内容不叫“Linux 服务器排错指南”,它叫 “How To Troubleshoot Common Site Issues on a Linux Server” ——标题里那个 “How To” 是核心。它不罗列命令,而是教你构建一套可复用的思维框架:当网站打不开、页面空白、上传失败、响应超时、数据库连不上时,你应该 按什么逻辑顺序去问问题、去哪里找证据、如何交叉验证、怎样避免被表象误导 。接下来的每一节,都会围绕一个高频故障场景展开,从现象切入,展示完整的排查链条,最后落回到一个可立即执行的验证动作。你不需要记住所有命令,但必须理解每一步背后的“为什么”。
2. HTTP 请求卡在哪儿?用四层诊断法穿透网络与服务
网站打不开,第一反应往往是“Nginx 挂了?”——这是最危险的直觉。真正的排查,必须从 OSI 模型的底层开始,逐层向上验证,因为任何一层的断裂都会导致上层“不可用”。我把它总结为 “四层诊断法” :网络层 → 传输层 → 应用层 → 内容层。跳过任何一层,都可能让你在错误的方向上浪费数小时。
2.1 网络层:确认物理/逻辑连通性,排除路由与防火墙干扰
这不是 ping 一下就完事。
ping example.com
成功,只说明 DNS 解析和 ICMP 路由通,但 HTTP 流量走的是 TCP 80/443 端口,它可能被完全阻断。
第一步:用
telnet
或
nc
直接测试目标端口
# 测试 80 端口(HTTP)
nc -zv example.com 80
# 测试 443 端口(HTTPS)
nc -zv example.com 443
nc
的
-z
参数表示只扫描端口不发送数据,
-v
输出详细信息。如果返回
Connection refused
,说明目标主机上没有服务监听该端口,或防火墙明确拒绝了连接。如果返回
timeout
,则是中间某处(云服务商安全组、本地 iptables/nftables、ISP 过滤)丢弃了包。
提示:很多新手会忽略云服务器的“安全组”配置。阿里云、腾讯云、AWS 的控制台里,安全组默认只放行 22 端口。即使你在服务器上
systemctl start nginx成功,外部请求依然会被云平台的虚拟防火墙拦截。务必检查安全组规则是否包含80/443的入方向(Inbound)TCP 规则。
第二步:在服务器本地验证端口监听状态 登录服务器后,别急着看 Nginx 日志,先确认服务是否真在监听:
# 查看所有监听的 TCP 端口及对应进程
ss -tlnp | grep -E ':80|:443'
# 或使用 netstat(部分新系统已弃用)
netstat -tlnp | grep -E ':80|:443'
关键看输出中的
State
列(应为
LISTEN
)和
PID/Program name
列。如果
ss
返回空,说明 Nginx/Apache 根本没启动,或者配置错误导致启动失败(此时
systemctl status nginx
会显示
failed
)。如果看到
LISTEN
,但
nc -zv localhost 80
仍失败,则极可能是 SELinux 或 AppArmor 在作祟——它们会在内核层面拦截 socket 绑定。
注意:
ss -tlnp需要 root 权限才能看到PID/Program name。普通用户执行会显示???。如果你用sudo ss -tlnp看到 Nginx 在监听*:80,但curl http://localhost返回Connection refused,那基本可以锁定是 SELinux 的http_port_t上下文被修改,或sebool httpd_can_network_connect被关闭。临时验证:sudo setsebool -P httpd_can_network_connect on,然后重试。
2.2 传输层:确认 TCP 连接建立成功,揪出 SYN Flood 或连接池耗尽
nc -zv
成功,只代表三次握手能完成,不代表 HTTP 请求能被处理。这里有个经典陷阱:
连接能建,但请求发不出去,或响应收不回来
。
第三步:用
tcpdump
抓包,看真实流量走向
# 在服务器上抓取 80 端口的进出包(需 root)
sudo tcpdump -i any port 80 -w /tmp/http.pcap -c 50
# 然后在本地用浏览器访问,再停止抓包
# 用 Wireshark 分析 /tmp/http.pcap,或用命令行快速查看
sudo tcpdump -r /tmp/http.pcap -A | grep -i "GET /"
如果抓包里能看到
SYN
包进来,但没有
SYN-ACK
回去,说明服务进程没响应,可能已僵死或被 OOM Killer 杀掉。如果能看到
SYN-ACK
,但后续
ACK
后没有
HTTP GET
,说明客户端(浏览器)没发请求,问题在客户端或中间代理。如果能看到
GET
请求进来,但没有
HTTP/1.1 200 OK
响应,那问题就锁定了:应用层服务(Nginx/Apache/PHP)收到了请求,但没能生成响应。
实操心得:我曾遇到一个案例,
ss -tlnp显示 Nginx 在监听,nc -zv localhost 80成功,但curl卡住。tcpdump显示请求进来了,响应却没出去。最后发现是 Nginx 配置里sendfile on;开启了,但后端 PHP-FPM 返回的Content-Length头与实际 body 长度不一致,导致内核sendfile()系统调用卡死。关掉sendfile立刻恢复。这说明,传输层的“通”,不等于应用层的“稳”。
2.3 应用层:验证 Web 服务器进程健康,区分“活着”与“可用”
systemctl status nginx
显示
active (running)
,不等于它能处理请求。进程可能处于僵尸态、CPU 占用 100% 无响应、或被 cgroup 限制了内存无法 fork 子进程。
第四步:用
ps
和
top
看进程真实状态
# 查看 Nginx 主进程和工作进程
ps aux | grep nginx
# 关键看 STAT 列:R=运行中,S=睡眠,Z=僵尸,< =高优先级,N=低优先级
# 如果看到大量 nginx: worker process 状态为 R,且 CPU% 持续 99%,说明它在忙死循环
# 如果看到 nginx: master process 状态为 S,但 worker 全是 Z,说明 master 没法回收僵尸 worker
# 实时监控资源占用
top -p $(pgrep -f "nginx: master") -p $(pgrep -f "nginx: worker")
# 按 Shift+M 按内存排序,Shift+P 按 CPU 排序
第五步:用
strace
追踪进程系统调用(终极手段)
当
top
显示进程在跑,但
curl
就是没响应,
strace
是你的显微镜:
# 追踪一个 worker 进程的系统调用(-p 后跟 PID)
sudo strace -p $(pgrep -f "nginx: worker" | head -1) -e trace=network,io -s 100 -T
# -e trace=network,io 只跟踪网络和 I/O 相关调用
# -s 100 显示字符串长度上限,避免截断
# -T 显示每次系统调用耗时
你会看到类似:
recvfrom(12, "\21\0\0\0\3SELECT VERSION()", 4096, 0, NULL, NULL) = 23 <0.000021>
sendto(12, "HTTP/1.1 200 OK\r\n...", 128, 0, NULL, 0) = 128 <0.000015>
如果
recvfrom
后长时间没
sendto
,说明它卡在了数据库查询或文件读取上。如果
recvfrom
本身就卡住,说明它在等客户端发数据,但客户端网络有问题。
踩坑实录:某次线上事故,
strace显示 Nginx worker 卡在open("/var/www/html/index.php", O_RDONLY)上长达 30 秒。ls -l /var/www/html/正常,cat /var/www/html/index.php也秒开。最后发现是/var/www挂载的 NFS 服务器负载过高,open()系统调用在内核 NFS 客户端里阻塞了。strace让我们 5 分钟内就定位到了 NFS 问题,而不是在 Nginx 配置里翻一整天。
2.4 内容层:确认响应内容正确,排除 PHP/Python/Node.js 层面的崩溃
Nginx 返回了
200 OK
,但浏览器显示空白页或
500 Internal Server Error
,问题就下沉到了应用代码层。这时,Web 服务器只是个“信使”,真正的“信”(HTML/JSON)没生成。
第六步:绕过 Web 服务器,直接调用应用解释器 对于 PHP:
# 直接用 CLI 模式运行 index.php,看是否报错
php -f /var/www/html/index.php
# 如果报错:Parse error: syntax error in /var/www/html/index.php on line 42
# 那就是代码语法错误,Nginx 日志里只会记一句 "PHP Parse error",根本找不到行号
对于 Python Flask/Django:
# 进入项目目录,用 Python 直接运行
cd /var/www/myapp
python app.py # 或 python manage.py runserver 0.0.0.0:5000 --noreload
# 然后 curl http://localhost:5000,看是否正常
如果 CLI 模式能跑,但通过 Nginx 就不行,那一定是 Web 服务器与应用的通信环节出了问题:PHP-FPM socket 权限不对、uWSGI 配置的 socket 路径不匹配、或环境变量缺失(如
PYTHONPATH
)。
关键经验:永远先用
curl -I(只获取 Header)和curl -v(显示完整请求/响应过程)来区分问题层级。curl -I http://localhost返回HTTP/1.1 500 Internal Server Error,说明 Nginx 收到了上游错误;如果curl -v显示* Connection #0 to host localhost left intact但没HTTP/1.1行,说明连接被重置,是传输层问题;如果curl -v显示HTTP/1.1 200 OK但curl http://localhost输出为空,那 99% 是应用代码里echo或
3. 日志不是“垃圾桶”,而是按时间戳排列的故障时间线
新手常犯的错误是:一出问题就
tail -f /var/log/nginx/error.log
,看到一堆
upstream timed out
就以为是后端慢,然后去优化数据库。结果修了一周,问题依旧。因为
error.log
只记录“发生了什么”,不记录“为什么发生”。真正的线索,往往散落在
系统日志、服务日志、应用日志、内核日志
四个地方,它们共同构成一条按纳秒精度排列的故障时间线。你得学会把它们“对齐”。
3.1 精确对齐日志时间戳:用
journalctl
作为中央时间轴
现代 Linux(systemd 系统)的日志统一由
journald
管理,它的时间戳精度是微秒级,且能关联所有服务。这是你排查的“主时间轴”。
第七步:用
journalctl
按时间范围拉取全量日志
# 查看最近 10 分钟所有服务的日志(含内核、systemd、nginx、php-fpm)
sudo journalctl --since "10 minutes ago" --all --no-pager | less
# 按服务过滤,看 nginx 和 php-fpm 在同一时刻的行为
sudo journalctl -u nginx -u php-fpm --since "2024-05-20 14:30:00" --no-pager
journalctl
的优势在于:它能把不同来源的日志按精确时间排序。比如你看到:
May 20 14:32:15 web01 nginx[1234]: *12345 connect() failed (111: Connection refused) while connecting to upstream
May 20 14:32:15 web01 php-fpm[5678]: ERROR: unable to bind listening socket for address '/run/php/php8.1-fpm.sock': No such file or directory
May 20 14:32:15 web01 systemd[1]: php8.1-fpm.service: Failed with result 'exit-code'.
这三行日志时间戳完全一致,立刻就能推断:Nginx 报错是因为 PHP-FPM 进程根本没起来,而 PHP-FPM 启动失败是因为它的 socket 文件路径
/run/php/php8.1-fpm.sock
对应的目录
/run/php/
不存在(
No such file or directory
)。
journalctl
把因果关系用时间戳钉死了。
提示:
journalctl默认只保存最近 1-2 周日志。生产环境务必配置持久化存储:# 编辑 /etc/systemd/journald.conf Storage=persistent SystemMaxUse=1G # 然后重启 journald sudo systemctl restart systemd-journald
3.2 Nginx 日志:读懂 access.log 里的“行为指纹”
access.log
不是流量统计工具,它是用户行为的原始录像带。每一行都包含 IP、时间、请求方法、URL、状态码、响应大小、Referer、User-Agent。
状态码是最高频的线索,但绝不能孤立看待
。
第八步:用
awk
+
sort
快速分析 access.log 异常模式
# 统计最近 1 小时 5xx 错误最多的 URL
sudo awk '$9 ~ /^5[0-9][0-9]$/ && $4 ~ /\[.*2024.*:..:..:../ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -10
# 查看某个 IP 的全部请求(排查爬虫或攻击)
sudo awk '$1=="192.168.1.100" {print}' /var/log/nginx/access.log | tail -20
# 找出响应时间 > 5 秒的请求($NF 是最后一个字段,即 $request_time)
sudo awk '$NF > 5 {print $0}' /var/log/nginx/access.log | tail -10
关键洞察:如果
502 Bad Gateway
都集中在
/api/v1/users
这个 URL,而其他接口正常,那问题肯定在
users
服务,而不是 Nginx 本身。如果
413 Request Entity Too Large
突然暴增,那一定是前端上传组件改了,或者 Nginx 的
client_max_body_size
被意外改小了。
实战技巧:Nginx 的
log_format可以自定义,强烈建议加入$request_time(处理时间)、$upstream_response_time(上游响应时间)、$upstream_addr(上游地址)。这样一行日志就能告诉你:是 Nginx 自己慢,还是上游慢,还是网络慢。配置示例:log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" ' '$request_time $upstream_response_time $upstream_addr';
3.3 Apache 日志:ErrorLog 里的“编译器错误”
Apache 的
error_log
比 Nginx 更“啰嗦”,但它会告诉你模块加载失败、语法错误、权限拒绝的每一个细节,像一个严格的 C 编译器。
第九步:用
grep
锁定 error_log 中的致命错误
# 查找最近的 FATAL、CRITICAL、CORE 错误(Apache 7.x+)
sudo grep -i "fatal\|critical\|core" /var/log/apache2/error.log | tail -10
# 查找模块加载失败(常见于 mod_php、mod_ssl)
sudo grep -i "module not found\|cannot load\|syntax error" /var/log/apache2/error.log | tail -10
# 查找权限相关错误(最常见!)
sudo grep -i "permission denied\|cannot access\|denied" /var/log/apache2/error.log | tail -10
一个典型例子:
[core:error] [pid 1234] (13)Permission denied: AH00035: access to / denied (filesystem path '/var/www/html') because search permissions are missing on a component of the path
。这行日志明确指出:
/var/www/html
目录的父目录(比如
/var/www
)缺少
x
(执行)权限,导致 Apache 进程无法
chdir
进入。
ls -ld /var/www
会显示
drw-r--r--
,缺了
x
。修复:
sudo chmod 755 /var/www
。
注意:Apache 的
DocumentRoot权限检查是递归的。/var/www/html有755不够,/var、/var/www都必须有x权限。chmod 755是安全底线,775或777是安全隐患。
3.4 内核日志:dmesg 里的“最后一声叹息”
当服务莫名退出、进程被杀、磁盘 IO 暂停,
dmesg
往往是唯一的目击者。它记录内核事件,包括 OOM Killer 的“处决令”。
第十步:用
dmesg -T
查看带人类可读时间的内核日志
# 查看最近的 OOM 事件(内存不足被杀)
sudo dmesg -T | grep -i "killed process"
# 查看磁盘错误(SMART 告警、IO timeout)
sudo dmesg -T | grep -i "error\|fail\|timeout\|nvme\|sd"
# 查看 SELinux 拒绝记录(如果启用了 enforcing 模式)
sudo dmesg -T | grep -i "avc:.*denied"
OOM Killer 的日志非常有特征:
[Mon May 20 14:32:15 2024] Out of memory: Kill process 12345 (php-fpm) score 892 or sacrifice child
[Mon May 20 14:32:15 2024] Killed process 12345 (php-fpm) total-vm:2048000kB, anon-rss:1536000kB, file-rss:0kB, shmem-rss:0kB
它清楚地告诉你:哪个进程(
php-fpm
)被杀了,为什么(
Out of memory
),以及它当时占了多少内存(
anon-rss:1536000kB
≈ 1.5GB)。这比
free -h
的快照更有价值,因为它记录了“死亡瞬间”的状态。
关键提醒:
dmesg日志是环形缓冲区,重启后会清空。所以一旦发现服务异常,第一时间dmesg -T > /tmp/dmesg.log保存现场。生产环境建议配置rsyslog将kern.*日志转发到远程日志服务器。
4. 资源瓶颈:磁盘、内存、inode,三个沉默的杀手
“网站打不开”背后,有 30% 的概率不是代码或配置问题,而是服务器资源耗尽。它们不会报错,只会让一切变慢、超时、拒绝服务。磁盘空间、内存、inode 数量,这三个指标就像服务器的“血压、血糖、血氧”,必须持续监控。
4.1 磁盘空间:
df
和
du
的战争,谁在说谎?
df -h
显示
/var
使用率 99%,但
du -sh /var/*
加起来才 10GB。这说明有“隐藏”的大文件或已删除但未释放的文件。
第十一步:找出“失踪”的磁盘空间
# 第一步:确认哪个挂载点满了
df -h | grep -E "(9[0-9]|100)%"
# 第二步:进入该目录,用 du 找大目录
cd /var
sudo du -sh * | sort -hr | head -10
# 第三步:如果 du 总和远小于 df,说明有被删除但进程还在占用的文件
# 查看所有被删除但仍被打开的文件(lsof -nP 会显示)
sudo lsof +L1 | grep "/var" # +L1 表示链接数为 0(即已删除)
# 或更直接:查找大文件
sudo lsof +L1 | awk '{if($7>1000000) print $0}' | sort -k7 -nr
典型场景:
/var/log/nginx/access.log
被 logrotate 重命名并压缩,但 Nginx 进程还拿着旧的文件描述符在写。
df
认为它还在,
du
却找不到它(因为文件名变了)。
lsof
会显示:
nginx 1234 root 1w REG 253,1 1234567890 123456 /var/log/nginx/access.log (deleted)
修复:
sudo kill -USR1 $(pgrep nginx)
发送 USR1 信号,让 Nginx 重新打开日志文件,释放旧句柄。
实操心得:我见过最离谱的案例是
/var/lib/php/sessions目录下,一个 PHP session 文件因session_write_close()没被调用,被锁住并不断追加数据,单个文件达 2.1GB。du扫描时跳过了被锁文件,df却如实计算。lsof是唯一能揪出它的工具。
4.2 内存:不只是
free -h
,还有
slabtop
和
smaps
free -h
显示
available
有 2GB,但
top
里 PHP 进程 RSS 占用 1.8GB,系统就开始 swap。这是因为
available
是内核估算的“可立即分配”内存,而
RSS
(Resident Set Size)是进程实际占用的物理内存。当
RSS
总和接近总内存,内核就会开始回收
page cache
,甚至
swap
。
第十二步:深入内存使用,定位“内存吞噬者”
# 查看各进程 RSS 占用(按内存排序)
ps aux --sort=-%mem | head -10
# 查看 slab 分配器使用(内核对象缓存,常被忽视)
sudo slabtop -o | head -20
# 查看某个进程的详细内存映射(如 PID 1234)
sudo cat /proc/1234/smaps | awk '/^Size:/ {sum += $2} END {print sum " kB"}'
slabtop
能揭示内核级内存泄漏。比如
dentry
(目录项缓存)或
inode_cache
占用飙升,说明有程序在疯狂创建/删除文件,或 NFS 客户端有 bug。
smaps
则能告诉你进程的
Size
(虚拟内存)、
RSS
(物理内存)、
PSS
(比例集大小,更公平的共享内存计量)。
关键参数:
/proc/sys/vm/swappiness控制内核使用 swap 的倾向。默认 60,意味着内存还剩 40% 就开始 swap。对于 Web 服务器,建议设为10或1:echo 'vm.swappiness=1' | sudo tee -a /etc/sysctl.conf sudo sysctl -p
4.3 inode:
df -i
被遗忘的维度
df -h
显示磁盘空间充足,但
touch test.txt
报错
No space left on device
。这时
df -i
会告诉你真相:inode 已用完(100%)。
第十三步:诊断和清理 inode
# 查看 inode 使用率
df -i
# 找出哪个目录 inode 最多(通常是日志、缓存、邮件队列)
sudo find /var -xdev -type f | cut -d "/" -f 2 | sort | uniq -c | sort -nr
# 清理 /var/spool/postfix/maildrop(Postfix 邮件队列积压)
sudo find /var/spool/postfix/maildrop -type f -mtime +7 -delete
# 清理 /var/log/journal(journald 日志)
sudo journalctl --vacuum-time=2weeks
inode 耗尽的典型场景:
/var/log/journal
目录下有数百万个
.journal~
临时文件;
/var/spool/postfix/maildrop
里堆积了数万封待发邮件;
/tmp
下有大量 PHP session 或 Composer 缓存文件。
提示:创建文件系统时,
mkfs.ext4的-i参数指定每多少字节分配一个 inode。默认是-i 16384(每 16KB 一个 inode)。如果预计要存海量小文件(如图片缩略图、session),应加大 inode 密度:mkfs.ext4 -i 4096 /dev/sdb1(每 4KB 一个 inode)。
5. 配置陷阱:语法、路径、权限,三个看似简单却致命的雷区
90% 的 Web 服务器启动失败,源于配置文件里一个标点符号、一个空格、一个路径错误。它们不会在
systemctl start
时报错,而是让服务静默失败,或在第一次请求时崩溃。
5.1 语法校验:
nginx -t
和
apachectl configtest
是你的编译器
永远不要跳过这一步。
nginx -t
不仅检查语法,还验证所有
include
的文件是否存在、所有
upstream
定义是否合法。
第十四步:执行配置校验,并解读错误信息
# Nginx
sudo nginx -t
# 如果报错:nginx: [emerg] invalid number of arguments in "proxy_pass" directive in /etc/nginx/sites-enabled/example.com:12
# 说明第 12 行 proxy_pass 写成了 proxy_pass http://backend/; (多了分号)
# Apache
sudo apachectl configtest
# 如果报错:Syntax error on line 42 of /etc/apache2/sites-enabled/example.com.conf:
# Invalid command 'SSLEngine', perhaps misspelled or defined by a module not included in the server configuration
# 说明 mod_ssl 没启用:sudo a2enmod ssl
nginx -t
的
-c
参数可以指定配置文件路径,用于测试新配置:
sudo nginx -t -c /tmp/new-nginx.conf
5.2 路径解析:
root
vs
alias
,
DocumentRoot
的绝对与相对
Nginx 的
root
和
alias
是新手最大误区。
root
是拼接路径,
alias
是替换路径。
第十五步:用
curl -v
验证路径映射是否正确
假设 Nginx 配置:
location /static/ {
alias /var/www/static/;
}
location /assets/ {
root /var/www;
}
那么:
-
curl http://example.com/static/logo.png→ 实际读取/var/www/static/logo.png -
curl http://example.com/assets/logo.png→ 实际读取/var/www/assets/logo.png
如果
alias
末尾少了
/
,
/static/
会映射到
/var/www/static
,导致
logo.png
被读取为
/var/www/staticlogo.png
(少了个
/
)。
Apache 的
DocumentRoot
必须是绝对路径,且 Apache 进程用户(通常是
www-data
或
apache
)必须对该路径有
x
权限(如前所述)。
实操验证:在配置文件里加一行
return 200 "root: $document_root, request_uri: $request_uri";,然后curl看输出,就能直观看到 Nginx 如何解析路径。
5.3 权限模型:用户、组、umask,三位一体的枷锁
Web 服务器进程以什么用户身份运行,决定了它能访问哪些文件。
ps aux | grep nginx
显示
www-data
,那
www-data
用户就必须对
DocumentRoot
有
r-x
(读和执行)权限,对
logs
目录有
w
(写)权限。
第十六步:用
namei
诊断路径权限
# 诊断 /var/www/html/index.php 的每一级权限
sudo namei -l /var/www/html/index.php
# 输出:
# f: /var/www/html/index.php
# drwxr-xr-x root root /
# drwxr-xr-x root root var
# drwxr-xr-x root root www
# drwxr-xr-x www-data www-data html
# -rw-r--r-- www-data www-data index.php
# 如果某一级(如 /var/www)显示 drw-r--r--,缺少 x,namei 会标红提示。
namei
是权限诊断的终极武器,它会逐级检查
r
(读)、
x
(执行,对目录是进入权限)、
w
(写)是否满足。
关键原则:文件权限
644(所有者 rw,组 r,其他 r),目录权限755(所有者 rwx,组 rx,其他 rx)。777是安全灾难。umask(默认 0022)决定了新创建文件的默认权限。确保nginx进程的umask是0022:在/lib/systemd/system/nginx.service的[Service]段添加UMask=0022。
6. 我的故障排查清单:一份可打印、可粘贴、可执行的行动纲领
写了这么多,你可能觉得步骤太多
更多推荐


所有评论(0)