强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Nginx 从入门到精通 / 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 aliastry_filesautoindex,基本缓存头
🟡 进阶sendfile 零拷贝,gzip_static 预压缩,ETag/Last-Modified
🔴 高级open_file_cacheaio 异步 I/O,directio,内核参数调优

下一章:Location 路由匹配 / Location Matching