Read only mode for webapps using Nginx
Put your webapp in read only or maintenance mode with Nginx
June 4, 2016
lua
redis
nginx
Quite often, I’ve had to put webapps in read only mode during maintenance activities like migrating databases, moving web servers, etc. During such windows, users are allowed to browse and read their existing data, but no write operations are allowed as they may lead to data inconsistency.
Most major frameworks have some sort of maintenance mode (including Django and Rails) which restricts the activity that a user is allowed to perform. However, I wanted a generic pattern that can be used across various platforms. Nginx being the awesome go-to tool that it is, allows one to leverage the power of Lua scripting with endless possibilities. The method described below relies on Nginx with a couple of plugins which make it easy to configure and extend.
So without further ado, here is gridlock
Requirements
- Nginx - all awesome reverse proxy
- lua-nginx-module - for embedding Lua scripts in our Nginx config
- Redis - to store configuration
- lua-rest-redis - reach our Redis configuration from Nginx
A easier way to get these in one shot is to install the openresty bundle, which comes with the required modules and is way easier to install.
Technique
The technique is simple - we use redis to set various keys, which are picked up by the Lua script. If the readonly mode is set, we then block all HTTP requests which modify a resource. So anything other than a GET and HEAD is blocked. You can set an option to determine how Nginx blocks the resource - either by throwing a 403 Forbidden error, or by setting a header for your app server to handle it however you deem fit. A custom header X-READONLY
is set to true so that the app can handle it accordingly.
Settings
The following redis keys control the behavior of the gatekeeper:
SET gridlock.readonly true
- Turn on readonly modeSET gridlock.readonly.mode redirect|forbid
- ifforbid
, nginx will exit with a 403 error. Ifredirect
, you can set a common error page to redirect toSET gridlock.readonly.redirect_url
- Define the redirect url for your app. If this is not set, and the mode is set toredirect
, we will redirect to the referer of the original requestLPUSH gridlock.readonly.allowed
- A list of URLs that you want to explicitly allow. These are for exceptions like login forms which you would still want to function
Once you have all the settings in place, you just flip the switch by setting gridlock.readonly
to true. The changes are live instantly without a reload! Once you maintainance window is done, simply set it to false, or delete the key. Zero downtime switching FTW!
Demo/Source
The code for the script and a simple Node.js app are available in the repo.
Warning
Although I’ve tested it out internally, absolutely no guarantees offered! If this breaks or burns your server, you are on your own.
Future
Currently, redis is hit everytime a request comes into Nginx. This obviously doesn’t work for high traffic sites. Figure out a way to use other lifecycle methods to periodically update and cache the settings.