田中ブログ

さいたまに住んでいる男です。仕事はSESでプログラムを書いてることが多いです。聖地巡礼、日々の生活、技術的なことを書いていこうと思います。

nginxのerror_pageとluaでerror_page内でステータスの変更・処理を行う

error_pageを指定しているはずなのに、期待したページが表示されなかったことがあったのでメモ。
やりたいことは以下です。
①404エラー時にステータスコードを502エラーとしてカスタムしたエラーページ(50x.html)を返す
②error_pageの中では条件によっては200を返す

環境としては、openrestyを利用してnginxとluaを使う感じです。

まずは①を試します。
通常は、error_pageの箇所に=502を記述し、リダイレクト先を/50x.htmlとすれば済みますが、最終的にlocationの中でいろいろやりたいので以下のように書いて試しました。

↓nginx.confの抜粋

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        error_page 404 /404.html;
        location = /404.html {
            return 502;
        }

この場合、curlでリクエストを投げるとopenrestyとバージョンが表示されたデフォルトっぽい自動で生成されたエラーページが表示されました。
(error_pageを書いてない場合、以下のフォーマットのページがデフォルトで表示されるのかな?)
curlとレスポンス

curl http://localhost:80/abc  -w '%{http_code}\n' -s
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>openresty/1.13.6.2</center>
</body>
</html>
404

ログを見ても/404.htmlにもアクセスしておらず、return 502でステータスコードが変わるわけでもなくダメダメの模様。(ちなみにreturn 502を消すと/404.htmlまではアクセスされた)

↓ログ

2019/05/03 15:27:55 [error] 141#141: *18 open() "/usr/local/openresty/nginx/html/abc" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /abc HTTP/1.1", host: "localhost"

これが動かなかった理由ですが、まずrecursive_error_pagesをONとしていなかったのが原因。
recursive_error_pagesはデフォルトでOFFになっており、error_page内で再度エラーとしてもハンドリングされないみたい。

以下のようにrecursive_error_pagesをONにしてやり、上位の404で処理するlocation内で、再度error_page404の記述をしてやれば動きました。
(下記の例のlocationはまったぐ意味がないが再帰的なerror_pageが動くということを示すためのもの。)

↓修正版

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # ここに定義が必要
        recursive_error_pages   on;

        error_page 404  /404.html;
        location = /404.html {
            error_page 404 =502 /50x.html;
        }

結果カスタムしたエラーページが返ってきてレスポンスコードも書き換わっている。

curlとレスポンス

 curl http://localhost:80/abc  -w '%{http_code}\n' -s
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the <a href="http://nginx.org/r/error_log">error log</a> for details.</p>
<p><em>Faithfully yours, OpenResty.</em></p>
</body>
</html>
502

これに加えてluaも利用したうえで記事の先頭の方で書いた①と②を実現する処理を書いてみる。
luaのコードを加えたnginx.conf

        location / {
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # これをONにすることでluaでexit(502)とした際も上記のerror_pageで処理される
        recursive_error_pages   on;

        error_page 404 @myError;

        location @myError {
            content_by_lua_block {
               if ngx.var.arg_flg == "1" then
                   ngx.status = 200;
                   -- execだけではstatusは書き換わらない
                   ngx.exec("/");
               else
                  return ngx.exit(502);
               end
            }
            # 404に対してerror_pageを書きたいときはここに記述
            #error_page 404 /50x.html;
        }

flg=1とした場合
curlとレスポンス

 curl "http://localhost:80/abc?flg=1"  -w '%{http_code}\n' -s
<!DOCTYPE html>
<html>
<head>
<title>Welcome to OpenResty!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to OpenResty!</h1>
<p>If you see this page, the OpenResty web platform is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="https://openresty.org/">openresty.org</a>.<br/>
Commercial support is available at
<a href="https://openresty.com/">openresty.com</a>.</p>

<p><em>Thank you for flying OpenResty.</em></p>
</body>
</html>
200

flg=2とした場合
curlとレスポンス

 curl "http://localhost:80/abc?flg=2"  -w '%{http_code}\n' -s
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the <a href="http://nginx.org/r/error_log">error log</a> for details.</p>
<p><em>Faithfully yours, OpenResty.</em></p>
</body>
</html>
404

意図したとおり、luaを利用してのerror_page内で条件によるステータスの変更、error_pageでも自動生成されるエラーページでなく、用意したエラーページが表示されることが確認できました!キモはrecursive_error_pages をONにすることでした。
また、error_pageをlocation内に再定義することで再帰的にerror_pageの処理もできるみたいですね。

以上