OpenResty + Luaを使うと、nginx単体ではできそうでできなかった細かな条件分けを構築することができます。
私が一番苦労したのは、静的コンテンツを時限公開することでした。
時刻で条件分けする
location /hoge {
default_type text/html;
content_by_lua_block {
-- 1675756800 = 2023/02/07 17:00:00
if (os.time() < 1675756800) then
ngx.say("このコンテンツは公開中です")
else
ngx.say("このコンテンツは非公開です")
end
ngx.say(ngx.localtime())
ngx.say(os.time())
}
}
結果
$ curl xxx.xxx.xxx.xxx/hoge
このコンテンツは公開中です
2023-02-07 16:59:27
1675756767
$ curl xxx.xxx.xxx.xxx/hoge
このコンテンツは非公開です
2023-02-07 17:02:25
1675756945
時刻でステータスコードをつける
nginx.status =
は ngx.say()の前にないと動きません。
これはハマりどころでしたが、HTTPのレスポンスの構造を考えると納得ではあります。
location /hoge {
default_type text/html;
content_by_lua_block {
if (os.time() < 1675756800) then
ngx.status = ngx.OK
ngx.say("このコンテンツは公開中です")
else
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("このコンテンツは非公開です")
end
ngx.say(ngx.localtime())
ngx.say(os.time())
}
}
結果
nginx.conf側の時刻をいじってテストしました。
$ curl -vvv "xxx.xxx.xxx.xxx/hoge"
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: openresty
< Date: Tue, 07 Feb 2023 08:56:37 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding
< Cache-Control: no-cache
<
このコンテンツは公開中です
2023-02-07 17:56:37
1675760197
* Connection #0 to host xxx.xxx.xxx.xxx left intact
$ curl -vvv "xxx.xxx.xxx.xxx/hoge"
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< Server: openresty
< Date: Tue, 07 Feb 2023 08:56:57 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding
<
このコンテンツは非公開です
2023-02-07 17:56:57
1675760217
* Connection #0 to host xxx.xxx.xxx.xxx left intact
時刻でリダイレクトする
ngx.redirect
で可能です。オプションをつけないと302リダイレクトされます。
301リダイレクトする場合には、それがキャッシュされることに気を付けてください。
公開前URLが露出したり、公開URLが再利用される環境で実装すると、一部ユーザーが、本来の公開時刻が過ぎてもページを閲覧できない可能性があります。
https://github.com/openresty/lua-nginx-module#ngxredirect
location /hoge {
default_type text/html;
content_by_lua_block {
if (os.time() < 1675756800) then
ngx.status = ngx.OK
ngx.say("このコンテンツは公開中です")
else
return ngx.redirect("http://example.com")
end
ngx.say(ngx.localtime())
ngx.say(os.time())
}
}
結果
リダイレクト側のみテストします。
$ curl -v "xxx.xxx.xxx.xxx/hoge"
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 302 Moved Temporarily
< Server: openresty
< Date: Tue, 07 Feb 2023 09:03:26 GMT
< Content-Type: text/html
< Content-Length: 142
< Connection: keep-alive
< Location: http://example.com
< Cache-Control: no-cache
<
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>openresty</center>
</body>
</html>
* Connection #0 to host xxx.xxx.xxx.xxx left intact
時限公開 + プロキシ
luaでレスポンスを生成する場合はcontent_by_lua_block
で処理をフックできますが、レスポンスがプロキシから生成される場合、その前段階、 rewrite_by_lua_block
で対応する必要があります。
ここもハマりどころです。
https://github.com/openresty/lua-nginx-module#directives の下部、Order of Lua Nginx Module Directives
図が参考になります。
location /hoge {
default_type text/html;
rewrite_by_lua_block {
if (os.time() < 1675760400) then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
proxy_pass https://example.com/;
}
結果
exitされる場合
OpenResty標準の403が返ってきます。
$ curl -v "xxx.xxx.xxx.xxx/hoge"
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< Server: openresty
< Date: Tue, 07 Feb 2023 09:37:24 GMT
< Content-Type: text/html
< Content-Length: 150
< Connection: keep-alive
<
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>openresty</center>
</body>
</html>
* Connection #0 to host xxx.xxx.xxx.xxx left intact
exitされずプロキシされた場合
proxy_pass
先の処理が戻ってきます。
$ curl --head -v "xxx.xxx.xxx.xxx/hoge"
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> HEAD /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: openresty
Server: openresty
< Date: Tue, 07 Feb 2023 09:31:53 GMT
Date: Tue, 07 Feb 2023 09:31:53 GMT
< Content-Type: text/html; charset=UTF-8
Content-Type: text/html; charset=UTF-8
< Connection: keep-alive
Connection: keep-alive
< Vary: Accept-Encoding
Vary: Accept-Encoding
....
< Set-Cookie: status=guest; path=/; domain=.example.com
Set-Cookie: status=guest; path=/; domain=.example.com
< Set-Cookie: v2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.example.com
Set-Cookie: v2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.example.com
...
<
* Connection #0 to host xxx.xxx.xxx.xxx left intact