HAProxy’s Lua scripting allows you to inject arbitrary logic into request and response processing, effectively extending its capabilities beyond its core proxying functions.
Here’s HAProxy processing a request with a Lua script:
-- haproxy.cfg
frontend http_frontend
bind *:80
mode http
http-request lua
http-response lua
default_backend http_backend
backend http_backend
mode http
server s1 127.0.0.1:8080
-- /etc/haproxy/lua/custom_logic.lua
local function handle_request(txn)
-- Get the request URI
local uri = txn.uri
-- Log the URI
ngx.log(ngx.INFO, "Processing request for URI: " .. uri)
-- Example: Redirect if URI starts with /old-path
if string.sub(uri, 1, 9) == "/old-path" then
txn.set_status(301) -- Permanent redirect
txn.add_header("Location", "/new-path" .. string.sub(uri, 10))
return
end
-- Example: Add a custom header
txn.add_header("X-Processed-By", "HAProxy-Lua")
end
local function handle_response(txn)
-- Log the response status
ngx.log(ngx.INFO, "Processing response with status: " .. txn.status)
-- Example: Modify response body (caution: can be complex)
-- This is a simplified example. For complex modifications,
-- consider using a dedicated response body manipulation tool
-- or ensuring your Lua script is efficient.
if txn.status == 200 then
local body = txn.body
if body then
txn.body = string.gsub(body, "example.com", "yourdomain.com")
end
end
end
-- Register the functions with HAProxy
haproxy.request.access(handle_request)
haproxy.response.access(handle_response)
To enable this, you’d typically configure HAProxy to load the Lua script:
# haproxy.cfg
global
lua-load /etc/haproxy/lua/custom_logic.lua
frontend http_frontend
bind *:80
mode http
http-request lua.handle_request # Call the request handler
http-response lua.handle_response # Call the response handler
default_backend http_backend
backend http_backend
mode http
server s1 127.0.0.1:8080
The http-request lua.<function_name> directive tells HAProxy to execute the specified Lua function (handle_request in this case) during the request processing phase. Similarly, http-response lua.<function_name> executes the Lua function (handle_response) during the response processing phase.
This system solves the problem of needing to perform complex, dynamic logic that is difficult or impossible to achieve with HAProxy’s native configuration directives alone. You can implement custom authentication, request filtering, dynamic routing based on obscure criteria, response manipulation, and more, all within the highly performant HAProxy process.
Internally, HAProxy embeds the Lua interpreter. When a http-request lua or http-response lua directive is encountered, HAProxy calls the registered Lua function. The txn object (transaction) passed to these functions provides access to request and response attributes like headers, URI, body, and status codes, as well as methods to modify them.
The key levers you control are:
- Lua script location: Where the
.luafile resides on the HAProxy server. lua-loaddirective: In theglobalsection, this tells HAProxy which Lua files to load at startup.http-request lua.<function_name>andhttp-response lua.<function_name>: Infrontendorbackendsections, these specify which Lua functions to execute at specific points in the request/response lifecycle.- Lua functions themselves: The actual code within your
.luafiles, defining the logic. You can have multiple functions and call them based on conditions.
The txn object is your primary interface. txn.uri, txn.headers, txn.status, and txn.body give you read access. Methods like txn.set_status(), txn.add_header(), txn.del_header(), txn.set_header(), txn.set_uri(), txn.set_body() allow modification. For more advanced control, you can also interact with HAProxy’s internal state using the ngx library, which provides functions for logging (ngx.log), sleeping (ngx.sleep), and more.
A common misconception is that Lua scripting adds significant overhead. While it’s not free, HAProxy’s Lua integration is highly optimized. The Lua interpreter is embedded, and the communication between HAProxy’s C core and the Lua VM is efficient. For most common tasks like header manipulation or simple conditional logic, the performance impact is negligible compared to the benefits of keeping that logic within the proxy. However, computationally intensive operations or blocking I/O within Lua will impact performance and potentially block HAProxy’s event loop. Use Lua for what it’s good at: fast, non-blocking logic.
The next step after mastering basic Lua scripting is to explore the haproxy.table API for dynamic data lookups and real-time configuration updates.