Lua dan OpenResty #
OpenResty adalah distribusi Nginx yang menyertakan interpreter LuaJIT dan kumpulan library Lua yang sudah dioptimalkan untuk aplikasi web. Ia memungkinkan kamu menulis logika yang kompleks langsung di dalam Nginx — tanpa harus meneruskan request ke backend terpisah hanya untuk operasi sederhana.
OpenResty vs Nginx + ngx_lua #
Ada dua cara menggunakan Lua dengan Nginx:
OpenResty adalah distribusi Nginx tersendiri yang sudah menyertakan semua yang dibutuhkan — LuaJIT, ngx_lua module, dan puluhan library Lua (redis, mysql, http client, JSON parser, dll.). Ini yang paling mudah digunakan.
ngx_lua adalah modul yang bisa dikompilasi ke Nginx standar. Lebih fleksibel tapi butuh lebih banyak setup manual.
Untuk sebagian besar kasus, OpenResty adalah pilihan yang lebih praktis.
Instalasi OpenResty #
# Ubuntu/Debian
sudo apt-get -y install --no-install-recommends wget gnupg ca-certificates
wget -O - https://openresty.org/package/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/openresty.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/ubuntu $(lsb_release -sc) main" \
| sudo tee /etc/apt/sources.list.d/openresty.list
sudo apt-get update
sudo apt-get install openresty -y
# Mulai OpenResty
sudo systemctl start openresty
Binary OpenResty ada di /usr/local/openresty/nginx/sbin/nginx dan konfigurasinya di /usr/local/openresty/nginx/conf/.
Fase Request Handling di Nginx #
Nginx memproses request melalui beberapa fase berurutan. ngx_lua menyediakan direktif untuk menyisipkan kode Lua di setiap fase:
Fase 1: rewrite_by_lua_block ← Rewrite URL, redirect
Fase 2: access_by_lua_block ← Autentikasi, otorisasi, rate limiting
Fase 3: content_by_lua_block ← Generate response konten
Fase 4: header_filter_by_lua_block ← Modifikasi response header
Fase 5: body_filter_by_lua_block ← Modifikasi response body
Fase 6: log_by_lua_block ← Custom logging setelah response terkirim
Hello World: Konten dari Lua #
server {
listen 8080;
location /hello {
content_by_lua_block {
ngx.header.content_type = "application/json"
ngx.say('{"message": "Hello from Lua!", "status": "ok"}')
}
}
# Atau dari file Lua terpisah (lebih baik untuk kode panjang)
location /api {
content_by_lua_file /etc/nginx/lua/api_handler.lua;
}
}
Autentikasi Token Sederhana #
server {
location /api/ {
access_by_lua_block {
local token = ngx.req.get_headers()["Authorization"]
if not token then
ngx.status = 401
ngx.header.content_type = "application/json"
ngx.say('{"error": "No token provided"}')
return ngx.exit(401)
end
-- Hapus prefix "Bearer "
token = token:match("^Bearer%s+(.+)$")
if not token or token ~= "secret-token-123" then
ngx.status = 403
ngx.header.content_type = "application/json"
ngx.say('{"error": "Invalid token"}')
return ngx.exit(403)
end
-- Token valid, lanjutkan ke backend
}
proxy_pass http://api_backend;
}
}
Dalam praktik, validasi token yang serius akan memeriksa token di Redis atau melakukan JWT verification menggunakan library Lua.
Rate Limiting dengan Redis #
Lua memungkinkan rate limiting yang lebih canggih dari bawaan Nginx — misalnya rate limit per user ID (bukan per IP), dengan counter yang disimpan di Redis:
# nginx.conf
http {
lua_shared_dict rate_limit_store 10m; # Bisa pakai shared dict (in-memory)
server {
location /api/ {
access_by_lua_file /etc/nginx/lua/rate_limit.lua;
proxy_pass http://backend;
}
}
}
-- /etc/nginx/lua/rate_limit.lua
local limit_req = require "resty.limit.req"
-- Buat limiter: 100 req/s, burst 50
local lim, err = limit_req.new("rate_limit_store", 100, 50)
if not lim then
ngx.log(ngx.ERR, "Failed to create limiter: ", err)
return ngx.exit(500)
end
-- Gunakan user ID dari header sebagai key (bukan IP)
local key = ngx.req.get_headers()["X-User-ID"] or ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
ngx.status = 429
ngx.header["Retry-After"] = "1"
ngx.say('{"error": "Too many requests"}')
return ngx.exit(429)
end
ngx.log(ngx.ERR, "Rate limit error: ", err)
return ngx.exit(500)
end
if delay >= 0.001 then
ngx.sleep(delay)
end
Akses Redis dari Lua #
http {
# Pool koneksi ke Redis
upstream redis_backend {
server 127.0.0.1:6379;
keepalive 32;
}
server {
location /cached/ {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000) -- connect, send, read (ms)
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "Redis connect failed: ", err)
ngx.exit(500)
return
end
local uri = ngx.var.uri
local cached = red:get("cache:" .. uri)
if cached and cached ~= ngx.null then
ngx.header.content_type = "application/json"
ngx.header["X-Cache"] = "HIT"
ngx.say(cached)
else
-- Cache miss: ambil dari backend, simpan ke Redis
-- (implementasi lebih kompleks di sini)
ngx.header["X-Cache"] = "MISS"
ngx.say('{"cached": false}')
end
-- Kembalikan koneksi ke pool
red:set_keepalive(10000, 100)
}
}
}
}
Kapan Menggunakan Lua di Nginx #
Lua di Nginx cocok untuk:
- Logika autentikasi yang perlu dijalankan di setiap request sebelum ke backend
- Rate limiting per user (bukan hanya per IP) dengan counter di Redis
- Request/response transformation ringan — ubah header, modifikasi body
- Feature flags yang bisa diambil dari Redis tanpa deployment ulang
- A/B testing dengan kondisi yang kompleks
Lua di Nginx kurang cocok untuk:
- Logika bisnis yang kompleks — kode lebih baik di backend
- Operasi database yang berat — gunakan backend
- Tim yang tidak familiar dengan Lua — menambah complexity operasional
Ringkasan #
- OpenResty adalah cara termudah menggunakan Lua di Nginx — sudah menyertakan semua library yang dibutuhkan.
- Nginx memproses request dalam fase berurutan —
access_by_lua_blockuntuk autentikasi,content_by_lua_blockuntuk generate response.resty.limit.reqdari library OpenResty memungkinkan rate limiting per user (bukan hanya per IP) dengan backend Redis.- Selalu kembalikan koneksi Redis ke pool dengan
set_keepalive()— tanpa ini, setiap request membuat koneksi baru.- Gunakan Lua untuk logika cross-cutting (autentikasi, rate limiting, feature flags) — bukan untuk logika bisnis yang kompleks.