03 - 静态文件服务 / Static File Serving
静态文件服务 / Static File Serving
🟢 基础 / Basics — 最简单的静态站点
基本配置
server {
listen 80;
server_name example.com;
root /var/www/example; # 网站根目录
index index.html; # 默认首页
location / {
try_files $uri $uri/ =404; # 先找文件,再找目录,都没有返回 404
}
}
# 创建目录和测试文件
sudo mkdir -p /var/www/example
echo '<h1>Hello Nginx!</h1>' | sudo tee /var/www/example/index.html
# 测试 & 重载
sudo nginx -t && sudo systemctl reload nginx
root vs alias 的区别
这是 Nginx 初学者最常混淆的概念:
# root:拼接完整路径 = root + location 路径
location /images/ {
root /var/www;
# 请求 /images/logo.png → 文件 /var/www/images/logo.png
}
# alias:替换 location 路径 = alias 本身就是完整路径
location /images/ {
alias /data/pics/;
# 请求 /images/logo.png → 文件 /data/pics/logo.png
}
记忆口诀:
root= 拼接(root + URI)alias= 替换(用 alias 路径替换 location 匹配的部分)
启用目录浏览
location /download/ {
alias /data/download/;
autoindex on; # 启用目录列表
autoindex_exact_size off; # 显示近似大小(KB/MB)而非精确字节
autoindex_localtime on; # 使用本地时间而非 UTC
}
效果:访问 /download/ 会显示文件列表,类似 FTP。
常见静态资源类型配置
server {
listen 80;
server_name example.com;
root /var/www/example;
# HTML 文件
location / {
try_files $uri $uri/ /index.html; # SPA 常用:找不到就返回 index.html
}
# 图片缓存
location ~* \.(jpg|jpeg|png|gif|ico|webp|svg)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# CSS/JS 缓存
location ~* \.(css|js)$ {
expires 7d;
add_header Cache-Control "public";
}
# 字体文件
location ~* \.(woff|woff2|ttf|eot)$ {
expires 365d;
add_header Cache-Control "public, immutable";
add_header Access-Control-Allow-Origin "*"; # CORS
}
}
🟡 进阶 / Intermediate — 生产环境静态服务配置
完整的静态站点配置
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 禁止访问隐藏文件(.git, .env 等)
location ~ /\. {
deny all;
return 404;
}
# 静态资源长期缓存(带 hash 的文件名)
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off; # 静态资源不记录日志,减少 I/O
}
# SPA 应用:所有未匹配的路由返回 index.html
location / {
try_files $uri $uri/ /index.html;
}
# 自定义 404 页面
error_page 404 /404.html;
location = /404.html {
internal;
}
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml;
}
try_files 详解
try_files 是 Nginx 最实用的指令之一:
# 语法:try_files file1 file2 ... last_uri;
# 1. 基本用法:找文件 → 找目录 → 404
try_files $uri $uri/ =404;
# 2. SPA 应用:找不到就返回 index.html(前端路由接管)
try_files $uri $uri/ /index.html;
# 3. 带 fallback 到后端
try_files $uri $uri/ @backend;
location @backend {
proxy_pass http://127.0.0.1:3000;
}
# 4. 返回特定状态码
try_files $uri =404; # 找不到直接 404
try_files $uri /fallback.html; # 找不到返回备用页面
try_files 执行流程:
请求: /assets/style.css
try_files $uri $uri/ /index.html;
1. 检查 /var/www/example/assets/style.css (文件) → 存在 → 返回
2. 检查 /var/www/example/assets/style.css/ (目录) → 不存在
3. 返回 /index.html(内部重定向)
sendfile 与零拷贝 / Zero-Copy
http {
sendfile on; # 启用 sendfile 系统调用(零拷贝)
tcp_nopush on; # 配合 sendfile,优化数据包发送
tcp_nodelay on; # 禁用 Nagle 算法,减少延迟
}
传统读写 vs sendfile:
传统模式(4 次拷贝):
磁盘 → 内核缓冲区 → 用户空间 → 内核 Socket 缓冲区 → 网卡
sendfile 零拷贝(2 次拷贝):
磁盘 → 内核缓冲区 → 网卡(直接 DMA)
↑
跳过用户空间,由内核直接传输
性能差异:小文件(<1MB)提升 2-3 倍,大文件提升 30%+。
Gzip 与预压缩 / Gzip & Pre-compression
实时压缩:
http {
gzip on;
gzip_vary on; # 添加 Vary: Accept-Encoding 头
gzip_proxied any; # 代理请求也压缩
gzip_comp_level 4; # 压缩级别 1-9(4 为性价比最高)
gzip_min_length 256; # 小于 256B 不压缩
gzip_buffers 16 8k; # 压缩缓冲区
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/json
application/xml
application/rss+xml
image/svg+xml
font/woff2;
gzip_disable "msie6"; # IE6 不压缩
}
预压缩(推荐生产环境):
http {
gzip_static on; # 优先返回 .gz 预压缩文件
gzip on; # 没有 .gz 文件时实时压缩
}
# 预压缩静态文件
find /var/www/example -type f \
\( -name "*.css" -o -name "*.js" -o -name "*.html" -o -name "*.svg" \) \
-exec gzip -k -9 {} \;
# 生成:
# /var/www/example/index.html ← 原文件
# /var/www/example/index.html.gz ← 预压缩版本
好处:避免每次请求都消耗 CPU 压缩,适合内容不常变的静态站点。
ETag 与 Last-Modified
# Nginx 默认自动添加这两个缓存验证头
# 禁用 ETag(如果你有自己的缓存策略)
etag off;
# 自定义 ETag 生成方式(Nginx 默认使用 last_modified + content_length)
etag on;
客户端缓存流程:
第一次请求:
GET /style.css
→ 200 OK
ETag: "64a-1234567890"
Last-Modified: Wed, 10 May 2026 08:00:00 GMT
[CSS 内容]
第二次请求(条件请求):
GET /style.css
If-None-Match: "64a-1234567890"
If-Modified-Since: Wed, 10 May 2026 08:00:00 GMT
→ 304 Not Modified(无 body,节省带宽)
🔴 高级 / Advanced — 性能极致优化
open_file_cache(文件描述符缓存)
http {
# 缓存文件元数据,避免每次请求都 stat() 系统调用
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 30s; # 每 30 秒验证一次缓存
open_file_cache_min_uses 2; # 至少访问 2 次才缓存
open_file_cache_errors on; # 缓存文件查找错误(如 404)
}
优化原理:
无 open_file_cache:
每次请求 → open() → stat() → read() → close()
↑ stat() 系统调用每次都执行
有 open_file_cache:
首次请求 → open() → stat() → 缓存元数据 → read()
后续请求 → 直接使用缓存 → read()(跳过 stat)
对于静态文件密集的站点(如 CDN),这可以提升 10-20% 的吞吐量。
大文件分片传输 / X-Accel-Range
# 支持断点续传和视频拖拽
location /video/ {
alias /data/video/;
# 启用分片
max_ranges 1;
# 禁用缓冲,支持大文件流式传输
sendfile on;
aio on; # 异步 I/O(Linux)
directio 512k; # 大于 512K 的文件使用 direct I/O(绕过页面缓存)
# 视频 MIME 类型
types {
video/mp4 mp4;
video/webm webm;
}
}
断点续传原理:
客户端: GET /video/movie.mp4
Range: bytes=10485760-
服务端: 206 Partial Content
Content-Range: bytes 10485760-20971519/20971520
Content-Length: 10485760
[从 10MB 位置开始的数据]
视频拖拽:
播放器 → 请求特定 Range → 服务器返回对应片段 → 继续播放
aio 与 directio(异步 I/O)
location / {
# 方式 1:线程池异步 I/O(推荐)
aio threads;
thread_pool default threads=32 max_queue=65536;
# 方式 2:Linux 原生 AIO(需要 directio)
# aio on;
# directio 512k;
sendfile on; # 小文件用 sendfile(零拷贝)
# 当文件 > directio 阈值时,自动切换到 aio
}
文件大小与 I/O 策略:
< 512KB: sendfile (零拷贝,最快)
≥ 512KB: aio + directio (异步,避免阻塞 Worker)
这样 Worker 线程不会因为读取大文件而被阻塞,
可以继续处理其他请求。
多磁盘 I/O 优化
# 如果有多块磁盘,可以将日志和静态文件分离
# 日志写入 SSD(高 IOPS)
access_log /ssd/nginx/logs/access.log;
error_log /ssd/nginx/logs/error.log;
# 静态文件放在 HDD(大容量)
location /files/ {
alias /hdd/data/files/;
sendfile on;
aio threads;
}
# 热点缓存放在 SSD
proxy_cache_path /ssd/nginx/cache levels=1:2 keys_zone=hot:100m;
Linux 内核参数调优
# /etc/sysctl.conf
# 文件描述符限制
fs.file-max = 655350
# TCP 优化
net.core.somaxconn = 65535 # listen 队列长度
net.core.netdev_max_backlog = 65535 # 网卡接收队列
net.ipv4.tcp_max_syn_backlog = 65535 # SYN 队列
net.ipv4.tcp_fin_timeout = 10 # FIN-WAIT-2 超时
net.ipv4.tcp_tw_reuse = 1 # 重用 TIME-WAIT 连接
net.ipv4.tcp_keepalive_time = 600 # Keepalive 探测间隔
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 10
# 端口范围
net.ipv4.ip_local_port_range = 1024 65535
# 缓冲区
net.core.rmem_max = 16777216 # 接收缓冲区最大值
net.core.wmem_max = 16777216 # 发送缓冲区最大值
# 应用生效
# sudo sysctl -p
# /etc/security/limits.conf
# Nginx worker 进程的文件描述符限制
* soft nofile 655350
* hard nofile 655350
小结 / Summary
| 层级 | 你需要知道的 / What You Need to Know |
|---|---|
| 🟢 基础 | root vs alias,try_files,autoindex,基本缓存头 |
| 🟡 进阶 | sendfile 零拷贝,gzip_static 预压缩,ETag/Last-Modified |
| 🔴 高级 | open_file_cache,aio 异步 I/O,directio,内核参数调优 |