我需要添加缓存策略到我的静态内容。
最初,我手动列出了所有我认为是静态的扩展,这几乎是每个答案所建议的:
location ~* \.(jpeg|jpg|gif|png|ico|css|bmp|js)$ {
# …
}
然后我会遇到一些新的扩展,或者意识到我忘记了一些,并更新配置:
location ~* \.(jpeg|jpg|gif|png|ico|css|bmp|js|cur|gz|svg|svgz|mp4|mp3|ogg|ogv|htc|woff2|woff)$ {
# …
}
我现在添加webm
和webp
,因此上述位置指令进一步增长:
location ~* \.(jpeg|jpg|gif|png|ico|css|bmp|js|cur|gz|svg|svgz|mp4|mp3|ogg|ogv|htc|woff2|woff|webm|webp)$ {
# …
}
而且由于还有更多现有和即将推出的扩展(来自。地图到.avif和.jxl),我不可能一一列举,这种硬编码方法似乎是一场没有终点的比赛。那么有没有一种面向未来的方法,可以制定一些location
指令,自动将 Nginx 从磁盘读取到客户端浏览器的所有现有(物理?)文件(即静态文件)作为目标?
答案1
无论如何,使用这样的正则表达式位置通常是不必要的,应尽可能避免。每个额外的正则表达式位置都会对性能产生额外的影响,尤其是在负载很重的系统上。由于正则表达式位置优先于前缀位置,因此最终由根位置处理的每个请求都会被检查是否与之前的每个正则表达式位置模式匹配。拥有大量正则表达式位置会显著影响整体服务器性能。
nginx 请求处理流程
总结首先,我想解释一下nginx的请求处理机制。请求处理阶段描述开发指南中的这些内容对于任何想要弄清楚的人来说都非常有帮助。看一下,因为我将在解释过程中参考这些阶段。我会尽量简要解释最重要的部分。
每个请求在处理过程中都会遍历多个位置块。它可以从一个位置传递到另一个位置,因为rewrite ... last
指令在(第一种显式方式)处被触发NGX_HTTP_REWRITE_PHASE
,根据指令的最后一个参数,try_files
当所有检查在NGX_HTTP_PRECONTENT_PHASE
(第二种显式方式)处失败时,或者通过index
指令在(隐式方式)的开头传递NGX_HTTP_CONTENT_PHASE
。实际上,请求至少还有一种方法可以通过使用error_page
指令声明的错误处理程序更改位置,但是我不会深入研究这种方法,因为它超出了本答案的范围。
rewrite
尽管如此,如果没有使用/指令或由于访问限制提前终止return
,每个请求最终都会到达NGX_HTTP_CONTENT_PHASE
最终都会到达某个位置并使用该精确的位置设置,尤其是内容处理器。它不会从已经传递的位置继承任何设置。内容处理程序可以明确指定(一些示例是http_proxy_module
使用proxy_pass
指令、http_fastcgi_module
使用fastcgi_pass
指令等),或者它将使用隐式附加http_static_module
(我会称它为静态内容处理程序)。
我所说的“通过指令隐式内部重定向”是什么意思index
?好吧,每个使用静态内容处理程序的位置都有一个NGX_HTTP_PRECONTENT_PHASE
等于try_files $uri $uri/ =404
指令的处理程序(如果没有使用try_files
指令和其他一些参数明确指定)。每个位置都有一个index
指令,要么明确声明,要么从上一个配置级别继承,要么具有默认值。也就是说,假设我们在或级别index index.html
没有任何明确定义的index
指令,则以下位置块是相等的:server
http
location / {
index index.html;
try_files $uri $uri/ =404;
}
location / {
try_files $uri $uri/ =404;
}
location / {}
如果 是位置下现有的物理目录(在本地文件系统上检查的实际文件路径将是和字符串的串联),则指令$uri/
的参数允许在当前位置内进行进一步的请求处理。稍后,在 期间,如果此目录中存在索引文件,则指令可以导致内部重定向。引用自try_files
$uri
root
$document_root
$uri
NGX_HTTP_CONTENT_PHASE
index
index
指令中的引文文档:
需要注意的是,使用索引文件会导致内部重定向,请求可以在不同的位置进行处理。例如,使用以下配置:
location = / { index index.html; } location / { ... }
请求
/
实际上将在第二个位置被处理/index.html
。
总结一下,如果你有如下配置
location / {
index index.php;
try_files $uri $uri/ /index.php$is_args_args;
add_header X-Content 'static';
}
location ~ \.php$ {
...
fastcgi_pass ... # fastcgi_module handler used here for content generation
}
- 由于正则表达式模式匹配,每个对 PHP 文件的请求都将直接进入 PHP 处理程序
\.php$
,因为正则表达式位置比前缀位置具有更高的优先级(除非使用^~
修饰符声明前缀位置); /index.php
由于内部重定向到指令发出,对不存在的文件或目录的每个请求都将转到 PHP 处理程序try_files
;- 由于内部重定向到指令发出,对包含文件的现有目录的每个请求
index.php
都将转到 PHP 处理程序。$uri/index.php
index
也就是说,只有现有的非 PHP 静态文件才有机会X-Content: static
在 HTTP 响应中获取该标头。
Web 应用程序的配置示例
PHP 驱动的应用程序
要为每个静态文件添加到期日期,可以使用以下配置:
location / {
index index.php;
expires 30d;
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
... php-fpm configuration
}
正如前面所说,这不会以任何方式影响任何 PHP 文件或“虚拟”应用程序路由。
由 JavaScript 框架(Angular/React/Vue/等)驱动的应用程序
此类 Web 应用程序通常使用类似以下的配置来提供服务:
location / {
try_files $uri /index.html;
}
该index.html
文件在此充当“路由控制器”的角色。要将其以及任何“虚拟”应用程序路由从缓存策略中排除,您可以将该位置一分为二:
location / {
expires 30d;
try_files $uri /index.html;
}
location = /index.html {
try_files $uri =404;
}
此外,您可以为不同的资产目录定义自定义缓存策略,例如:
location / {
expires 30d;
try_files $uri /index.html;
}
location /assets/ {
expires 90d;
try_files $uri =404;
}
location = /index.html {
try_files $uri =404;
}
然而,当涉及到 JavaScript 驱动的 Web 应用程序时,您可能也希望从缓存中排除 javascript 文件,下面我们将进入答案的下一部分和最后一部分。
针对不同文件类型的不同缓存策略
好的,你说,这一切都很有趣,但我需要针对不同的文件类型使用不同的缓存策略,并且有些静态文件我根本不想被缓存,例如 javascript 文件。这意味着我仍然必须对这些文件类型使用正则表达式位置,对吗?
不,很可能不是。
令人遗憾的是,互联网上有很多使用这种方法的例子,包括来自受人尊敬的来源的例子,比如官方 Web 应用程序文档中的部署建议。还有另一种更有效的方法可以做到这一点。
你可以通过 nginx 发送的 MIME 类型值来评估你的缓存策略(以及许多其他位置设置),作为Content-Type
mime.types
响应 HTTP 标头。该值将从主配置文件中包含的文件中获取nginx.conf
(通常位于/etc/nginx
目录中),您可以通过$sent_http_content_type
nginx 内部变量。我们以 nginx 部署配置为例受到推崇的Joomla 文档并使其性能更高。而不是
server {
...
location / {
try_files $uri $uri/ /index.php?$args;
}
...
location ~* \.(ico|pdf|flv)$ {
expires 1y;
}
location ~* \.(js|css|png|jpg|jpeg|gif|swf|xml|txt)$ {
expires 14d;
}
}
您可以使用map
块来评估expires
指令值(此处使用的 MIME 类型取自 nginx 1.21.4 默认mime.types
文件,将来可能会发生变化;请检查您自己的mime.types
文件以了解实际值):
map $sent_http_content_type $expires {
image/x-icon 1y;
application/pdf 1y;
video/x-flv 1y;
application/javascript 14d;
text/css 14d;
image/png 14d;
image/jpeg 14d;
image/gif 14d;
application/x-shockwave-flash 14d;
text/xml 14d;
text/plain 14d;
default off;
}
然后,您可以使用这个单个非正则表达式位置,而不是上面显示的三个位置:
server {
...
location / {
expires $expires;
try_files $uri $uri/ /index.php?$args;
}
...
}
有人可能会问,删除 6 行配置并添加 15 行有什么问题?我们的配置只会变得更大吗?或者更有趣的是,嘿,我可以map
使用正则表达式模式减少该块以包含每种图像类型,如下所示:
map $sent_http_content_type $expires {
image/x-icon 1y;
~^image/ 14d;
...
}
是的,你可以。不要这么做。这与配置大小无关,而是关于表现,这应该是你的首要目标(至少我是这么认为的)。当map
表仅包含字符串时,它在内部变为哈希表,其中欧拉(1)评估时间复杂度。当您向其中添加正则表达式时,它会被分成两部分,一个固定值的哈希表和一个正则表达式模式列表。如果与哈希表部分没有完全匹配,则源值将逐一与列表中的所有正则表达式模式进行匹配,直到找到第一个匹配项或整个列表完成。也就是说,如果性能确实是您的主要目标,那么您不会这样做(但是对于这种特殊情况,由于用一个正则表达式匹配操作替换两个正则表达式匹配操作,因此会有一些性能优势)。