Skip to content

Instantly share code, notes, and snippets.

@danackerson
Last active October 20, 2023 10:11
  • Star 78 You must be signed in to star a gist
  • Fork 23 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save danackerson/af7481a869396839b3da to your computer and use it in GitHub Desktop.
using nginx + lua + redis for redirects and rewrites
# using such a setup requires `apt-get install lua-nginx-redis` under Ubuntu Trusty
# more info @ http://wiki.nginx.org/HttpLuaModule#access_by_lua
http {
lua_package_path "/etc/nginx/include.d/?.lua;;";
lua_socket_pool_size 100;
lua_socket_connect_timeout 10ms;
lua_socket_read_timeout 10ms;
server {
listen 80;
server_name www.stylight.com;
location / {
include /etc/nginx/include.d/en_US/rewrites/122_lua_st_redis_rewrites.lua;
}
}
}
access_by_lua '
local redis = require "redis"
local red = redis:new()
-- see all available redis: methods @ https://github.com/openresty/lua-resty-redis#methods
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "failed to connect to redis: ", err)
return ngx.exit(500)
end
red:select(1)
local redirect_candidate = ngx.var.uri
local args_remainder = ngx.null
local args = ngx.var.args
-- we store internal application rewrites as hash values in Redis for their corresponding pretty URL keys
-- if a bot crawls through asking for /search.action?brand=3210
local hash_rewrite, error = red:hgetall(ngx.var.uri)
-- 127.0.0.1:6379[1]> hgetall /search.action?brand=3210
-- (error) WRONGTYPE Operation against a key holding the wrong kind of value
-- let's find the appropriate redirect URL:
if error then
if args then
local m = ngx.re.match(args, [[^(gclid=.*)?(?<start>.*(shop|brand|tag)=[0-9]+)+&?(.*(shop|sale|searchstring|gender)=.*?&?)*(|&(?<remainder>.*))$]])
if m then
if m["start"] then
redirect_candidate = redirect_candidate .. "?" .. m["start"]
end
if m["remainder"] then
args_remainder = m["remainder"]
end
end
end
-- and ask Redis for the 301 redirect URL
local rewrite_value, err = red:get(redirect_candidate)
-- 127.0.0.1:6379[1]> get "/search.action?brand=3210"
-- "/Mexx/"
-- create/use a connection pool of size 100 with 0 idle timeout
red:set_keepalive(0, 100)
if rewrite_value ~= ngx.null then
if args_remainder ~= ngx.null then
rewrite_value = rewrite_value .. "?" .. args_remainder
end
-- and kindly ask the bot to visit /Mexx/ instead
ngx.redirect(rewrite_value, 301);
end
else
-- a request for /Sandals/Women/ however
if table.getn(hash_rewrite) > 0 then
red:set_keepalive(0, 100)
-- gives us the following hash from Redis:
-- 127.0.0.1:6379[3]> hgetall /Sandals/Women/
-- 1) "action"
-- 2) "search.action"
-- 3) "params"
-- 4) "gender=women&tag=10580&tag=10630"
-- lets transform to an internal rewrite
arr_rewrite = red:array_to_hash(hash_rewrite)
params = nil
if arr_rewrite["params"] then
params = arr_rewrite["params"]
end
if args then
if params~=nil then
params = params .. "&" .. args
else
params = args
end
end
if params~=nil then
ngx.req.set_uri_args(params)
end
-- and send off to our app server for processing
ngx.req.set_uri("/" .. arr_rewrite["action"])
end
end
';
@roguh
Copy link

roguh commented Jul 27, 2023

Note that this requires the Nginx Lua module, so Nginx must be compiled with it or it must dynamically load the Lua module. I've used the fabiocicerchia/nginx-lua Docker image, as well as Kubernetes' ingress-nginx.

I'd recommend using access_by_lua_file instead of access_by_lua so you can store the Lua script in its own pure-Lua file. This helps with syntax highlighting and any code quality tools you might use.

@danackerson
Copy link
Author

very valid points @roguh!

for context (since timestamps are a bit obfuscated on gists), I wrote/used this script back in 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment