使用Nginx提供静态文件服务:优化技巧

通过正确的缓存头、Gzip压缩、安全默认设置以及保护隐藏文件的技巧,优化Nginx静态文件传输,让网站更快,减少过时资源带来的困扰。

使用Nginx提供静态文件服务:优化技巧

使用Nginx提供静态文件服务是传输图片、CSS、JavaScript、下载文件和前端构建产物最常见且高效的方式之一。Nginx非常擅长这项工作,但一些配置选择可能会决定你的网站是快速且可预测,还是浪费带宽或提供过时内容。

静态文件优化主要涉及清晰的路径、正确的缓存头、压缩和安全默认设置。你不需要复杂的配置就能获得显著效果,但缓存规则必须与文件的命名和部署方式相匹配。

从清晰的静态文件位置开始

最简单的静态文件设置使用roottry_files

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/public;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

使用此配置,对/css/app.css的请求将映射到/var/www/example.com/public/css/app.css。如果文件不存在,Nginx返回404

这种直接映射在调试时很有用。你可以从浏览器获取URL,将其转换为文件系统路径,并检查文件是否存在:

ls -l /var/www/example.com/public/css/app.css

如果文件存在但Nginx返回404,请检查是否存在不同的root、更具体的location块或覆盖路径的包含文件。

对于单页应用,你可能希望未知路由回退到index.html

location / {
    try_files $uri $uri/ /index.html;
}

这对于前端路由器很有用,但不要盲目用于每个网站。如果丢失的资源也返回index.html,调试损坏的JavaScript或图片路径可能会变得混乱。许多团队为资源使用单独的location,以便丢失的文件仍然返回真正的404

当URL路径应映射到不同的文件系统路径时,你也可以使用alias

location /assets/ {
    alias /srv/shared-assets/;
}

注意尾部斜杠。使用alias时,location路径和文件系统路径通常都应以/结尾。不匹配可能会产生意外的文件路径。

一个安全的模式是:

location /downloads/ {
    alias /srv/downloads/;
    try_files $uri =404;
}

这里/downloads/manual.pdf映射到/srv/downloads/manual.pdf。如果没有尾部斜杠的规范,很容易意外构建出不存在的路径或暴露你不想发布的目录。

有关匹配行为的更深入探讨,请参阅Nginx location块

添加浏览器缓存头

静态文件是浏览器缓存的绝佳候选。如果用户下载了app.css一次,浏览器就不应在每次页面浏览时重新获取它,除非它发生了变化。

对于版本化的资源,使用较长的缓存生命周期:

location /assets/ {
    root /var/www/example.com/public;
    expires 1y;
    add_header Cache-Control "public, immutable";
}

这在部署期间文件名发生变化时效果最佳,例如app.8f3a91.cssbundle.20260523.js。如果文件名在内容更改时发生变化,浏览器可以安全地长时间缓存旧文件。

对于保持相同名称的文件,使用较短的缓存:

location = /index.html {
    root /var/www/example.com/public;
    expires -1;
    add_header Cache-Control "no-cache";
}

这种模式在前端应用中很常见。HTML文件保持新鲜,而哈希化的CSS和JavaScript文件则被积极缓存。

一个实际的例子:你的React或Vue构建在/assets/中输出哈希化的资源和一个普通的index.html。将/assets/缓存一年,但让index.html重新验证。用户获得快速的重复访问,新部署仍然加载最新的资源引用。

更改缓存规则后,测试头部而不是猜测:

curl -I https://example.com/assets/app.8f3a91.css
curl -I https://example.com/

你希望哈希化的资源显示一个长生命周期的Cache-Control值。你通常希望HTML页面重新验证或使用短生命周期。如果两者都被缓存一年,部署可能会使用户停留在指向旧JavaScript的旧HTML文件上。

对文本资源使用压缩

文本资源如CSS、JavaScript、SVG和JSON压缩效果很好。你可以在http块中启用Gzip:

gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_types
    text/css
    application/javascript
    application/json
    image/svg+xml;

不要期望Gzip对JPEG、PNG、WebP、MP4或zip文件有太大帮助。这些格式已经压缩过。再次尝试压缩它们通常会浪费CPU。

对于高流量的静态网站,考虑预压缩文件。你的构建过程可以为大型CSS和JavaScript文件创建.gz版本,当浏览器支持Gzip时,Nginx可以提供它们:

gzip_static on;

这减少了运行时压缩工作,因为Nginx从磁盘读取预压缩文件。当资源是提前构建的并且在请求之间不发生变化时,这最有用。

压缩只是资源交付的一部分。文件大小仍然很重要。在构建过程中删除未使用的JavaScript、优化图片,并避免发送用户不需要的大型文件。

当你测试压缩时,包含一个Accept-Encoding头:

curl -I -H 'Accept-Encoding: gzip' https://example.com/assets/app.js

查找Content-Encoding: gzipVary: Accept-Encoding。当CDN或共享缓存位于Nginx之前时,Vary头很重要,因为压缩和未压缩的响应不能混淆。

改善文件交付和安全性

Nginx可以使用默认设置高效地提供静态文件,但在生产环境中,一些细节会有所帮助。

首先,除非明确需要,否则禁用目录列表:

autoindex off;

目录列表可能会泄露你无意发布的文件名和结构。

其次,阻止访问隐藏文件,如.env.git和其他点文件:

location ~ /\.(?!well-known) {
    deny all;
}

.well-known的例外很常见,因为证书验证和基于标准的文件可能使用该目录。

第三,确保你的静态文件权限允许Nginx读取文件,但不要给Web服务器不必要的写入权限。典型的设置允许部署工具写入文件,并允许Nginx工作进程读取它们。

第四,检查MIME类型。Nginx通常包含一个mime.types文件,但精简的容器或自定义构建可能会缺少它。如果CSS作为text/plain提供,浏览器可能会拒绝它或行为异常。

使用:

include /etc/nginx/mime.types;
default_type application/octet-stream;

最后,监控日志中重复的资源404响应。这通常意味着部署引用了不存在的文件,缓存仍然指向旧文件名,或者alias路径错误。

如果静态交付感觉缓慢,不要一开始就复制你能找到的每个调优指令。首先检查问题是否真的是Nginx。大型图片、未优化的前端包、远程存储挂载和CDN缓存未命中比服务器块中缺少微优化更常见。

对于快速的本地检查:

curl -o /dev/null -s -w 'status=%{http_code} size=%{size_download} time=%{time_total}\n' https://example.com/assets/app.js

然后将其与CDN日志、浏览器开发者工具或来自用户同一区域的请求进行比较。Nginx的快速响应和浏览器的慢速响应通常指向交付路径中的其他地方。

何时寻求帮助

如果你的静态文件来自共享存储、挂载卷、对象存储网关或Nginx前面的CDN,请引入DevOps工程师。最佳的缓存策略取决于整个交付路径,而不仅仅是Nginx服务器块。

如果用户在部署后报告过时的JavaScript,你也应该寻求帮助。这通常意味着缓存规则和文件名版本化策略不匹配。

使用Nginx提供静态文件服务的最佳实践是:路径可预测,缓存生命周期与文件名策略匹配,文本资源被压缩。保持丢失文件可见,保护隐藏文件,并在每次配置更改后测试头部。一个干净的静态设置可以让你的网站更快,而无需增加额外的移动部件。