Nginx redirects using Lua

For years, I've been a big fan of DuckDuckGo's bang feature. I use it any time I have a specific site I want to search. For example, if I know the result I want to get is on Wikipedia, you can simply use !w [search term] rather than having to see a search result page or having to go to Wikipedia and using their search box. You wouldn't think the extra click matters, but it adds up.

However, with DuckDuckGo, if you do not put a ! at the beginning of your search, it will act like any other search engine and show you a page of results.

Google is still the king of getting relevant search results, so I often find myself switching between the two. So much so, I used DuckDuckGoog which provides google search results if you do not use the bang feature but redirects you to DuckDuckGo if you do. I've found it to be the best of both worlds.

Today, DuckDuckGoog is down. It's a fairly straightforward hack to check if your search query starts with a !, so I decided to host my own. I had never done so in the past because it seemed like such a trivial service to host. It hardly seems worth it to load up a PHP script or redirect to a NodeJS page.

I settled on using NGINX's Lua scripting capabilities. It seemed like the right choice because it gets you as close to the webserver as possible but frees you from the limitations of NGINX's config language. It's available in the nginx-extras package on any debian derived system like Ubuntu or Mint.

You'll need to place this section of config of whatever host you are serving it from.

NGINX configuration

location /search {
  include /path/to/duckduckgoog.lua;
}

Here's the script I came up with. It needs to be placed in a file the webserver has access to.

Lua

access_by_lua "
local url = ngx.var.uri
local args = ngx.var.args

function starts(str, start)
   return string.sub(str, 1, string.len(start)) == start
end

function split(str, pat)
   local t = {}  -- NOTE: use {n = 0} in Lua-5.0
   local fpat = '(.-)' .. pat
   local last_end = 1
   local s, e, cap = str:find(fpat, 1)
   while s do
      if s ~= 1 or cap ~= '' then
         table.insert(t,cap)
      end
      last_end = e+1
      s, e, cap = str:find(fpat, last_end)
   end
   if last_end <= #str then
      cap = str:sub(last_end)
      table.insert(t, cap)
   end
   return t
end

local q = split(args, '=')[2]

if starts(q, '!') then
    local ddg_url = 'https://duckduckgo.com/?q='
    -- print(ddg_url .. q)
    ngx.redirect(ddg_url .. q, 301)
else
    local goog_url = 'https://www.google.com/search?q='
    -- print(goog_url .. q)
    ngx.redirect(goog_url .. q, 301)
end
";