使用Nginx提供静态文件服务:优化技巧
通过正确的缓存头、Gzip压缩、安全默认设置以及保护隐藏文件的技巧,优化Nginx静态文件传输,让网站更快,减少过时资源带来的困扰。
使用Nginx提供静态文件服务:优化技巧
使用Nginx提供静态文件服务是传输图片、CSS、JavaScript、下载文件和前端构建产物最常见且高效的方式之一。Nginx非常擅长这项工作,但一些配置选择可能会决定你的网站是快速且可预测,还是浪费带宽或提供过时内容。
静态文件优化主要涉及清晰的路径、正确的缓存头、压缩和安全默认设置。你不需要复杂的配置就能获得显著效果,但缓存规则必须与文件的命名和部署方式相匹配。
从清晰的静态文件位置开始
最简单的静态文件设置使用root和try_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.css或bundle.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: gzip和Vary: 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提供静态文件服务的最佳实践是:路径可预测,缓存生命周期与文件名策略匹配,文本资源被压缩。保持丢失文件可见,保护隐藏文件,并在每次配置更改后测试头部。一个干净的静态设置可以让你的网站更快,而无需增加额外的移动部件。