Nginxで、POSTメソッドがエラー405で弾かれる場合

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 ^~ /real/ {
・・・
}
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って・・気づかなかったことに。