Untested by me
coinkite/cloudfire
If the new visitor does not have a session cookie (managed by Lua, not your app), then a boring HTML page is rendered. That HTML page contains Javascript to do a "proof of work" exercise and then posts the result back to Lua. If the proof-of-work verifies, the user gets a cookie and can proceed onto their usual content.