Nginxで、GETは通るけれど、POSTが通らず、
ステータスコード 405: Method Not Allowed(許可されていないメソッド)
が返される場合。
原因:
Nginxは、スタティックなページでは、POSTが使えません。
スタティックなページ:ファイルの内容をそのまま返す場合など。
(というよりも、デフォルトでは全てスタティックと思ってよい感じです。)
対策(まとめ):
急いでいる時の応急処置: エラー405を200に付け替える。
(本当のエラーも付け替える事になるので、応急処置です。)
ちゃんとした対策: 外から見える側のlocationで、POSTを受けられるようにする。
例えば、openrestyを使っている場合は、**ngx.req.get_post_args()**などを呼んでおく。
(そのlocationから、ngx.exec()でどこかに飛ばす場合も、呼び出し元のlocationで
get_post_args()などを呼んでおく必要があります。)
処理後に固定の結果を返す場合でも、ngx.exec()は使えません。
ngx.exec()を使うと、POSTが使えないlocationに転送される場合はエラー405が発生します。
ngx.say()は使えます。(POSTが使えるlocationから外に出ていないので。)
回避方法 その1:
とにかく、とりあえず通したい時に。
(dirty hack..と書かれているけれど)
エラー405 を200(OK)に付け替えます。
参考ページの回答欄から。
error_page 405 =200 $uri;
回避方法(??) その2:
参考ページのコメント欄から。
If nginx's certain location contains proxy_pass or fastcgi_pass directive, this is a dynamic content, otherwise -- static.
要約:(略)・・fastcgi_passを使っていないlocationは、スタティックになります。
proxy_passにしてみる。
・・・
}
location ^~ /p/ {
proxy_pass http://127.0.0.1/real/;
}```
405ではじかれます。
アクセス先が動的に変わるだけなので、アクセス先がスタティックな内容なら、
処理はスタティックなページと同じです。
ということで、proxy_pass案は却下。
----
fastcgi_passにしてみる・・のがよさそうですが・・
Nginx wiki
[PHPFcgiExampleJa](http://wiki.nginx.org/PHPFcgiExampleJa)
> NginxはFCGIプロセスを自動的には生成しません。
そのためFCGIプロセスは別途実行させる必要があります。
要するに、別枠で(サーバとして)起動しておいて、用があったらCGIとして呼んでね!
(このあと、無茶苦茶○○した という流れになるわけで。)
使いたい言語で、サーバ機能が使えるといいですね・・(遠い眼)
例えば、POSTしたデータを、データベースに流し込みたい時。
・・Apacheで!と言われそうなので考えるのはやめておこう・・
Nginx->CGI->PHP->データベース でもよいのですが、
別枠で処理するのは無駄なので、POSTをあきらめたほうがよさそうです。
Nginx->(openresty)->データベース の環境で使いたいので。
(クエリでキャッシュが汚染されそうですが)
分解して、GETで渡してしまうのが妥当な線かな・・・と。
URIに含めたくない情報が入るのが難点ですが。
(特に、間にCDNがはさまっている場合。)
参考ページ:
Stackoverflow
[POST request not allowed - 405 Not Allowed - nginx, even with headers included](http://stackoverflow.com/questions/24415376/post-request-not-allowed-405-not-allowed-nginx-even-with-headers-included
)
----
めも1:
[openresty/lua-resty-upload](https://github.com/openresty/lua-resty-upload)
multipart/form-data で送る。
form:read() で、こける。
----
めも2:
ngx.req.get_post_args() とか、
[ngx.req.get_body_data()](https://github.com/openresty/lua-nginx-module#ngxreqget_post_args)
なら行ける。
application/x-www-form-urlencoded で送る。
ただし、外からアクセスするためのlocationに、
POSTされたデータの受け口を置かないといけないので、
データベースアクセス用のlocationと分けている場合は、使いにくい。
(外からアクセスされる側でGET用のクエリなどを組み立てて、
データベースアクセス用のlocationで、クエリを分解しないといけないので。
大きなデータの場合は、ファイルに落としておいて、パスを渡すなど。)
・・と思いましたが、GETのクエリが残る現象から、
(locationの中で変更して`@name`に渡しても、変更前のクエリが渡る)
POSTも残っているのでは?と思って実験してみたら、
しっかり残っていました。
外からアクセスされる側のlocationでPOSTを受けられるようにしておいて、
(外からアクセスされる側のlocationでngx.req.get_post_args()などを実行した後に)
再度、データベースアクセス用のlocationでPOSTのデータを読めば良いようです。
※外からアクセスされる側で、ngx.req.read_body()しただけでは、エラー405が発生します。
ちなみに、外からアクセスされる側で、ngx.req.discard_body()しても、
データベースアクセス用のlocationでngx.req.get_post_args()すると、
データが取得できました。
テスト用。
init_by_lua '
cjson = require "cjson"
';
外から見える部分。
フォームのデータを受け取ります。
ngx.req.get_post_args()の行をコメントアウトすると、エラー405になるはず。
argsを分解するのが面倒なので、cjsonに流し込む・・という手抜き仕様です。
location ^~ /test/ {
default_type application/json;
content_by_lua '
ngx.req.read_body()
local args, err = ngx.req.get_post_args()
ngx.req.discard_body()
if not args then
ngx.say("{}")
do return end
end
--ngx.say(cjson.encode(args))
-- DO NOT use ngx.say() before ngx.exec()
-- Call another(internal) location
ngx.exec("/for/db/")
';
}
内部用(データベース用など)
@name にすると、GETのパラメータが外れる(外からアクセスした時のものになる)ので注意しましょう。
動作確認用なので、データを表示したら終了するコードです。
location ^~ /for/db/ {
interrnal;
default_type application/json;
# Get POST body data
content_by_lua '
ngx.req.read_body()
local args, err = ngx.req.get_post_args()
ngx.req.discard_body()
if not args then
ngx.say("{}")
do return end
end
ngx.say(cjson.encode(args))
';
}
----
おまけ:
参考: [nginxのproxy_passの注意点](http://www.xmisao.com/2014/05/09/nginx-proxy-pass.html)
proxy_passで、サーバの(IP)アドレスだけを指定する場合と、後続のパスがある場合で動作が変わります。
アドレスだけを指定した場合は、アドレスを付け替えます。
パスがある場合は、パスを付け替えます。
`http://example.com/some/place/foo`にアクセスした時、
例1:
```location ^~ /some/place/ {
proxy_pass http://127.0.0.1
}```
の場合は、`http://127.0.0.1/some/place/foo`に接続します。
※IPアドレスだけを付け替えるイメージです。
例2:
```location ^~ /some/place/ {
proxy_pass http://127.0.0.1/
}```
の場合は、`http://127.0.0.1/foo`に接続します。
※`/some/place/`を`/`に置き換えるイメージです。
例3:
```location ^~ /some/place/ {
proxy_pass http://127.0.0.1/other/place/
}```
の場合は、`http://127.0.0.1/other/place/foo`に接続します。
※`/some/place/`を`/other/place/`に置き換えます。
例4:
```location ^~ /some/place/ {
proxy_pass http://127.0.0.1/other/place
}```
の場合は、`http://127.0.0.1/other/placefoo`に接続します。
※`/some/place/`を`/other/place`に置き換えます。
はい・・予想外です!
とならないように注意しましょう。
えっと、お約束のあれを。
サーバの数珠つなぎでの情報共有をなくそう。 あっ、Webって・・気づかなかったことに。