Building Pastebin for text using NGINX and Lua

At times I use different services for sharing text or code snippets. Pastebin is a well known term for such services (where you paste whatever you want to share). The functionality is also simple (mostly) and that’s why I decided to build a pastebin service using only NGINX.

NGINX and Lua

I have been a fan of NGINX the moment I learnt of it. In my earlier days (about 5 yrs ago), I was an Apache guy. But the moment I became familiar with NGINX and its vast power, I never looked at any other webserver software.

I often share text or code snippets online and that is why, It was fun to build it without using any “backend”. NGINX alone was enough with its awesome Lua module which offers unlimited possibilities. I wanted to make it simple so there is not much in it other than the only feature of storing plain-text pastes.

NGINX Config

The whole backend of this pastebin is inside of the NGINX config. The Lua code within content_by_lua_block is all responsible for saving the paste. The code is saving the pastes to the local filesystem but there are endless possibilities (for example, storing in a database like mysql or redis).

The following location block is responsible for all /api/pastes requests. The directory to which the paste is saved must also be writeable by the webserver user.

# api for storing paste
location = /api/pastes {
	default_type "text/plain";

		client_max_body_size 128k;
		client_body_buffer_size 128k;
			
	content_by_lua_block {
		ngx.req.read_body()
		
		local method = ngx.req.get_method()

		if method == "POST" then
			local args, err = ngx.req.get_post_args(1)

			if err == "truncated" then
				ngx.say("error: only one POST parameter allowed.")
				ngx.exit(403)
			end

			if not args then
				ngx.say("error: failed to get post args: ", err)
				ngx.exit(500)
			end
			
			local content = args.content

			if content == nill then
				ngx.say("error: required parameter 'content' not set")
				ngx.exit(400)
			elseif content == "" then
				ngx.say("error: required parameter 'content' is empty")
				ngx.exit(400)
			end

			math.randomseed(os.clock()+os.time())
			local random = math.random
			local function randid()
				local template ='xxxx4xxxxxxyxxxx'
				return string.gsub(template, '[xy]', function (c)
					local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
					return string.format('%x', v)
				end)
			end
			local name = randid()
			local file, err = io.open("/var/www/nginx-pastebin/pastes/" .. name, "w")
			if file == nill then
				ngx.say("error: couldn't open file: ", err)
			else 
				file:write(content)
				file:close()
			end
			
			ngx.say("https://", ngx.var.host, "/", name)

		elseif method == "GET" then
			ngx.say("send a post request with 'content=your_text_content'")
		else 
			ngx.say("error: not allowed")
			ngx.exit(405)
		end
	}
}

This is an example of how services involving simple (or even complicated?) logic can be written using Lua inside NGINX.

ngx has many other APIs and you can perform almost every NGINX-related action in your Lua code (rewriting, redirects, caching, serving content and much more).

Demo and Complete Code

I have set this up on pastebin.usamaejaz.com and the source code has been pushed to github.com/usamaejaz/nginx-pastebin.

Written on June 30, 2018