如果在nginx的location块中同时使用limit_except和try_files,会导致出现404 Not Found错误

准备上游

准备一个简单的Sinatra应用程序来响应GET /app和POST /app。

$ rbenv local 2.3.0
$ cat <<EOF > Gemfile
source 'https://rubygems.org'

ruby '2.3.0'

gem 'sinatra'
gem 'thin'
gem 'rack-cors', require: 'rack/cors'
EOF
$ bundle install
$ cat <<EOF > app.rb
require 'sinatra'

get '/app' do
  'GET'
end

post '/app' do
  'POST'
end
EOF
$ cat <<EOF > config.ru
require './app'
run Sinatra::Application
EOF
$ bundle exec rackup -p 3000
$ curl http://localhost:3000/app
GET
$ curl -X POST http://localhost:3000/app
POST

准备使用try_files指令进行内部重定向。

如果URL路径没有对应的文件,则将其重定向到上游。

$ curl -L -O http://downloads.sourceforge.net/project/pcre/pcre/8.38/pcre-8.38.tar.bz2
$ tar -xjvf pcre-8.38.tar.bz2
$ nginx-build -d . -clear --with-pcre=$(pwd)/pcre-8.38
$ mkdir logs conf
$ cp nginx/1.9.15/nginx-1.9.15/conf/mime.types ./conf
$ cat <<EOF > conf/nginx.conf
worker_processes 1;

events {
    worker_connections 1024;
}

http {
    include mime.types;

    upstream app {
        server localhost:3000;
    }

    server {
        listen 8000;
        server_name localhost;
        root html;

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

        location @app {
            proxy_pass http://app;
        }
    }
}
EOF
$ mkdir html
$ echo html > html/index.html
$ ./nginx/1.9.15/nginx-1.9.15/objs/nginx -p $(pwd) -c conf/nginx.conf
$ curl http://localhost:8000/index.html
html
$ curl http://localhost:8000/app
GET
$ curl -X POST http://localhost:8000/app
POST

設置基本認證

在限制条件之前,无论请求方法如何,尝试对所有内容进行基本认证。

$ cp conf/nginx.conf conf/nginx.conf.orig
$ vi conf/nginx.conf
$ diff -u conf/nginx.conf.orig conf/nginx.conf
--- conf/nginx.conf.orig    2016-04-23 16:31:35.000000000 +0900
+++ conf/nginx.conf 2016-04-23 16:48:22.000000000 +0900
@@ -18,6 +18,8 @@

         location / {
             try_files $uri $uri/index.html $uri.html @app;
+            auth_basic "Basic auth";
+            auth_basic_user_file .htpasswd;
         }

         location @app {
$ echo "user:$(openssl passwd -apr1 password)" > conf/.htpasswd
$ ./nginx/1.9.15/nginx-1.9.15/objs/nginx -p $(pwd) -c conf/nginx.conf -s reload
$ curl -u 'user:password' http://localhost:8000/app
GET
$ curl -X POST -u 'user:password' http://localhost:8000/app
POST

只允许使用 limit_except 来对 POST 请求进行基本认证。

只有在POST请求方法时才需要进行基本认证。

$ vi conf/nginx.conf
$ diff -u conf/nginx.conf.orig conf/nginx.conf
--- conf/nginx.conf.orig    2016-04-23 16:31:35.000000000 +0900
+++ conf/nginx.conf 2016-04-23 16:51:08.000000000 +0900
@@ -18,6 +18,10 @@

         location / {
             try_files $uri $uri/index.html $uri.html @app;
+            limit_except GET {
+                auth_basic "Basic auth";
+                auth_basic_user_file .htpasswd;
+            }
         }

         location @app {
$ ./nginx/1.9.15/nginx-1.9.15/objs/nginx -p $(pwd) -c conf/nginx.conf -s reload
$ curl http://localhost:8000/app
GET
$ curl -i -X POST -u 'user:password' http://localhost:8000/app
HTTP/1.1 404 Not Found
Server: nginx/1.9.15
Date: Sat, 23 Apr 2016 07:51:33 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive

<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.9.15</center>
</body>
</html>

404 页面未找到

为什么?

顺便说一下,不管是使用基本身份验证还是与之相关的,只要使用limit_except指令,就会直接返回404 Not Found。

$ vi conf/nginx.conf
$ diff -u conf/nginx.conf.orig conf/nginx.conf
--- conf/nginx.conf.orig    2016-04-23 16:31:35.000000000 +0900
+++ conf/nginx.conf 2016-04-23 16:54:31.000000000 +0900
@@ -18,6 +18,8 @@

         location / {
             try_files $uri $uri/index.html $uri.html @app;
+            limit_except GET {
+            }
         }

         location @app {
$ ./nginx/1.9.15/nginx-1.9.15/objs/nginx -p $(pwd) -c conf/nginx.conf -s reload
$ curl http://localhost:8000/app
GET
$ curl -i -X POST -u 'user:password' http://localhost:8000/app
HTTP/1.1 404 Not Found
Server: nginx/1.9.15
Date: Sat, 23 Apr 2016 07:51:33 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive

<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.9.15</center>
</body>
</html>

将 “limit_except” 移动到另一个 “location”。

使用limit_except指令在try_files指令的内部重定向位置指令中,便不会返回404 Not Found。

$ vi conf/nginx.conf
$ diff -u conf/nginx.conf.orig conf/nginx.conf
--- conf/nginx.conf.orig    2016-04-23 16:31:35.000000000 +0900
+++ conf/nginx.conf 2016-04-23 16:58:00.000000000 +0900
@@ -21,6 +21,10 @@
         }

         location @app {
+            limit_except GET {
+                auth_basic "Basic auth";
+                auth_basic_user_file .htpasswd;
+            }
             proxy_pass http://app;
         }
     }
$ ./nginx/1.9.15/nginx-1.9.15/objs/nginx -p $(pwd) -c conf/nginx.conf -s reload
$ curl http://localhost:8000/app
GET
$ curl -X POST -u 'user:password' http://localhost:8000/app
POST

总结

这是怎样的情况呢?
可能是我找文档的方法有问题,找不到相关的说明。
如果阅读源代码的话可能会明白,但是因为觉得麻烦,所以想用一种简单却行得通的方法来应付。

广告
将在 10 秒后关闭
bannerAds