Lua dan OpenResty

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 berurutanaccess_by_lua_block untuk autentikasi, content_by_lua_block untuk generate response.
  • resty.limit.req dari 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.

← Sebelumnya: Modul Bawaan   Berikutnya: Modul Pihak Ketiga →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact