Nginx 位置,try_files 和 headers 问题

Nginx locations, try_files and headers issue

所以我用 Puma 替换了 Passenger 作为 Rails 应用程序,我刚刚注意到我现在的 cdn 资产有问题,它们现在给出 CORS 错误。

当我使用 Passenger 时,我对 Nginx 进行了以下配置:

server {

  server_name mysite.com;
  root /var/www/mysite.com/public;

  client_max_body_size 4000M;
  passenger_enabled on;
  rails_env production;

  location ~* ^/cdn/ {
    add_header Access-Control-Allow-Origin *;
    expires 364d;
    add_header Pragma public;
    add_header Cache-Control "public";
    break;
  }

  location ~* ^/assets/ {
    # Per RFC2616 - 1 year maximum expiry
    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
    expires 1y;
    add_header Cache-Control public;

    # Some browsers still send conditional-GET requests if there's a
    # Last-Modified header or an ETag header even if they haven't
    # reached the expiry date sent in the Expires header.
    add_header Last-Modified "";
    add_header ETag "";
    break;
  }

  listen 443 ssl; # managed by Certbot
  #the rest of the certbot ssl stuff

}

然后我将配置更改为此,使其适用于 Puma 和 unix 套接字:

upstream puma {
  server unix:///var/www/mysite.com/shared/sockets/puma.sock;
}
server {

  server_name mysite.com;
  root /var/www/mysite.com/public;

  client_max_body_size 4000M;

  location / {
    try_files $uri @app;
  }

  location ~* ^/cdn/ {
    add_header Access-Control-Allow-Origin *;
    expires 364d;
    add_header Pragma public;
    add_header Cache-Control "public";
    break;
  }

  location ~* ^/assets/ {
    # Per RFC2616 - 1 year maximum expiry
    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
    expires 1y;
    add_header Cache-Control public;

    # Some browsers still send conditional-GET requests if there's a
    # Last-Modified header or an ETag header even if they haven't
    # reached the expiry date sent in the Expires header.
    add_header Last-Modified "";
    add_header ETag "";
    break;
  }

  listen 443 ssl; # managed by Certbot
  #ssl stuff

  location @app {
    proxy_pass http://puma;

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Host $http_host;

    proxy_headers_hash_max_size 512;
    proxy_headers_hash_bucket_size 128;

    proxy_redirect off;
  }

}

这工作正常,但后来我注意到 cdn url 给出了 404,所以我将 cdn 位置更新为此(我添加了 try_files $uri @app;):

  location ~* ^/cdn/ {
    add_header Access-Control-Allow-Origin *;
    expires 364d;
    add_header Pragma public;
    add_header Cache-Control "public";
    try_files $uri @app;
    break;
  }

这现在有效,但我收到 CORS 错误,所以似乎 headers 没有设置。

我的猜测是 try_files 忽略了调用之前设置的内容,所以我尝试在 location @app 中设置代理 header 以进行访问控制(通过添加 proxy_set_header Access-Control-Allow-Origin *;) 但我仍然收到错误。

解决这个问题的正确方法是什么?

尝试使用 proxy_set_header 指令添加响应 headers 是没有意义的 - 它是为 adding/changing headers 设计的被传送到上游。要添加响应 headers,无论是静态位置还是代理位置,请使用 add_header 响应。要有条件地添加 headers,假设根据请求 URI,您可以使用 map 块:

map $uri $expires {
    ~^/cdn/  1y;
    default  off;
}
map $uri $cache_control {
    ~^/cdn/  public;
    # default will be an empty value
}
map $uri $allow_origin {
    ~^/cdn/  *;
    # default will be an empty value
}

然而,就性能而言,由于所有 map 派生的变量在每个请求中只被评估一次,因此只匹配一次正则表达式模式可以稍微提高性能:

map $cache $expires {
    1        1y;
    default  off;
}
map $cache $cache_control {
    1        public;
}
map $cache $allow_origin {
    1        *;
}
map $uri $cache {
    ~^/cdn/  1;
}

接下来,在您的 @app 位置,您可以使用以下内容:

location @app {
    proxy_pass http://puma;

    expires $expires;
    add_header Cache-Control $cache_control;
    add_header Access-Control-Allow-Origin $allow_origin;

    # ... proxy_set_... and other upstream setup here
}

如果 add_header 指令中使用的评估变量为空,nginx 将不会添加具有空值的 header - 相反它不会添加这样的 header完全没有。


关于您当前配置的几点注意事项:

  • 使用像 location ~ ^/cdn/ { ... }location ~ ^/assets/ { ... } 这样的正则表达式位置来支持前缀位置 location /cdn/ { ... }location /assets/ { ... } 是没有意义的,只是性能影响(由于在不需要时涉及 PCRE 库)。

  • 静态位置末尾的 break 指令不执行任何操作,因为重写模块中没有任何应该停止执行的指令。