在一个post请求内 ngx.exec 回提示 405 Not Allowed

306 views
Skip to first unread message

raymond wang

unread,
Sep 13, 2013, 2:55:30 AM9/13/13
to open...@googlegroups.com
背景信息是这样的:
我想做一个访问防火墙,根据ip地址,每分钟内访问超过一定次数用ngx.exec("/captcha") 返回一个验证码页面。

但是如果刚好达到这个次数时是一个post请求的话,会提示405错误,经过验证,在post请求内之行ngx.exec就会返回405,求问怎么解决?

Yichun Zhang (agentzh)

unread,
Sep 13, 2013, 2:50:32 PM9/13/13
to openresty
Hello!

2013/9/12 raymond wang:
> 背景信息是这样的:
> 我想做一个访问防火墙,根据ip地址,每分钟内访问超过一定次数用ngx.exec("/captcha") 返回一个验证码页面。
>
> 但是如果刚好达到这个次数时是一个post请求的话,会提示405错误,经过验证,在post请求内之行ngx.exec就会返回405,求问怎么解决?
>

你确定 405 是由于 POST 请求 + ngx.exec 引起的吗?

我在本地尝试了下面这个最小化的例子,使用最新的 ngx_lua 0.8.8 结合 nginx 1.4.2 运行,并不能复现你说的问题:

location = /t {
content_by_lua '
ngx.req.read_body()
ngx.exit(403)
';
}

我们来用 curl 发送一个 POST 请求给这个 location /t:

$ curl -i -d 'hello world' localhost:8080/t
HTTP/1.1 403 Forbidden
Server: nginx/1.4.2
Date: Fri, 13 Sep 2013 18:47:49 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.4.2</center>
</body>
</html>

从输出可以看到,是期望的 403 响应。

如果你确实可以确认是这种组合造成的问题,请尝试提供一个最小化的完整示例,这样我可以在本地复现你看到的问题。谢谢合作!

Regards,
-agentzh

raymond wang

unread,
Sep 17, 2013, 3:45:42 AM9/17/13
to open...@googlegroups.com
hi agentzh:
我发现如果ngx.exec的资源url是个静态html资源的话就会出现我说的405问题,你试试下面这个最小化完整示例:

server {
    listen 8082;

    location = /t {
        default_type 'text/plain';
        content_by_lua '
            return ngx.exec("/index.html")
        ';
    }

    location = /exec {
        default_type 'text/plain';
        content_by_lua '
            ngx.say("exec")
        ';
    }
}

└> curl -i -d 'hello world' http://127.0.0.1:8082/t
HTTP/1.1 405 Not Allowed
Server: ngx_openresty/1.2.8.6
Date: Tue, 17 Sep 2013 07:45:31 GMT
Content-Type: text/html
Content-Length: 182
Connection: keep-alive

<html>
<head><title>405 Not Allowed</title></head>
<body bgcolor="white">
<center><h1>405 Not Allowed</h1></center>
<hr><center>ngx_openresty/1.2.8.6</center>
</body>
</html>

Yichun Zhang (agentzh)

unread,
Sep 17, 2013, 3:08:05 PM9/17/13
to openresty
Hello!

2013/9/17 raymond wang:
> 我发现如果ngx.exec的资源url是个静态html资源的话就会出现我说的405问题,你试试下面这个最小化完整示例:
[...]
> location = /t {
> default_type 'text/plain';
> content_by_lua '
> return ngx.exec("/index.html")
> ';
> }
>

嘿,我就知道你很可能会犯这样的错误 :)

简单地说,这是因为 nginx 服务静态文件的标准模块看到了 POST 请求方法就立即扔出了 405 错误页。这与 ngx_lua 模块并没有任何关系。

>
> └> curl -i -d 'hello world' http://127.0.0.1:8082/t
> HTTP/1.1 405 Not Allowed

你如果尝试一下直接 POST 给 /index.html,你也会得到 405 错误页:

$ curl -i -d 'hello world' http://127.0.0.1:8082/index.html
HTTP/1.1 405 Not Allowed
Server: nginx/1.4.2
Date: Tue, 17 Sep 2013 19:06:13 GMT
Content-Type: text/html
Content-Length: 182
Connection: keep-alive

<html>
<head><title>405 Not Allowed</title></head>
<body bgcolor="white">
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx/1.4.2 (no pool)</center>
</body>
</html>

显然,Nginx 自己的静态文件服务模块认为这里 POST 给一个静态资源不是一个好主意 :)

Regards,
-agentzh

jianpin...@gmail.com

unread,
Sep 18, 2013, 2:04:14 AM9/18/13
to open...@googlegroups.com

之前有个项目,再做压力测试的使用,使用nginx  模拟了后端服务,有些服务时post 请求 ,也出现了405 错误当时使用了

 error_page  405 =200 405;
        location 405
        {
         root  lua/B9ServerTest/;
         }

解决的。但愿对你有用。

在 2013年9月13日星期五UTC+8下午2时55分30秒,raymond wang写道:

jianpin...@gmail.com

unread,
Sep 18, 2013, 2:04:24 AM9/18/13
to open...@googlegroups.com

之前有个项目,再做压力测试的使用,使用nginx  模拟了后端服务,有些服务时post 请求 ,也出现了405 错误当时使用了

 error_page  405 =200 405;
        location 405
        {
         root  lua/B9ServerTest/;
         }

解决的。但愿对你有用。

在 2013年9月13日星期五UTC+8下午2时55分30秒,raymond wang写道:
背景信息是这样的:

raymond wang

unread,
Sep 18, 2013, 7:09:48 AM9/18/13
to open...@googlegroups.com
好吧,看来是我无知了,我去想想其他的解决方法。

多谢春哥!

Yichun Zhang (agentzh)

unread,
Sep 18, 2013, 2:41:02 PM9/18/13
to openresty
Hello!

2013/9/18 raymond wang:
> 好吧,看来是我无知了,我去想想其他的解决方法。
>

如果你非得 POST 给 nginx 的静态文件服务模块,你可以在 Lua 里面修改当前请求的请求方法,以欺骗 nginx 的静态文件服务模块,即:

location = /t {
default_type 'text/plain';
content_by_lua '
if ngx.req.get_method() == "POST" then
ngx.req.set_method(ngx.HTTP_GET)
end
return ngx.exec("/index.html")
';
}

不过,有一个问题是,这会导致你的 nginx 访问日志里面记录的请求方法是你修改后的方法。对于这个问题的一个绕过的方法是引入一个自己的
$orig_method 变量,在你修改请求方法之前把原来的方法名保存进 $orig_method,最后在 log_format 模版中使用
$orig_method 变量,而不是 $method 变量。

祝玩得开心 :)

Regards,
-agentzh
Reply all
Reply to author
Forward
0 new messages