Rack::Rewrite for Site Maintenance and Downtime

Rack::Rewrite is a Rack middleware for defining and applying rewrite rules. Though it's not a full replacement for Apache's mod_rewrite, a great deal of rules I've previously written in Apache config files can be replaced by Rack::Rewrite. Run gem install rack-rewrite to install the gem.

I typically leverage rewrite rules to take my sites offline for maintenance. Most capistrano users will be familiar with the following Apache rewrite ruleset.

  RewriteCond %{REQUEST_URI} !\.(css|jpg|png)$
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
  RewriteRule ^.*$ /system/maintenance.html [L]

This ruleset matches requests for non-asset URL's and renders a maintenance page if it exists on the filesystem. When capistrano users run cap deploy:web:disable REASON="site upgrade" UNTIL="2PM" a maintenance page is placed in public/system and this ruleset begins to kick in. Running cap deploy:web:enable will remove this page and the ruleset ceases to match.

We can replace this ruleset with the following Rack::Rewrite rule:

  # Ruby 1.8.x
  maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
  send_file /.*/, maintenance_file, :if => Proc.new { |rack_env|
    File.exists?(maintenance_file) && rack_env['REQUEST_URI'] !~ /\.(css|jpg|png)/

This rewrite rule uses the send_file method to return the maintenance page, uses a rule guard (the :if proc) to check for the existence of the file, and accesses the rack environment directly (rack_env arg to the Proc) to check the request URI. Due to the shortcomings of the Ruby 1.8's regular expression library (no negative lookahead), we have to leverage the rule guard to allow assets to continue to be served (css, jpg, png).

Using Ruby 1.9, this rule is simpler.

  # Ruby 1.9.x
  maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
  send_file /(.*)$(?<!css|png|jpg)/, maintenance_file, :if => Proc.new { |rack_env|

Users of 1.8.x can leverage Oniguruma to keep the rule simpler.

  # Ruby 1.8.x + Oniguruma
  maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
  send_file Oniguruma::ORegexp.new("(.*)$(?<!css|png|jpg)"), maintenance_file, :if => Proc.new { |rack_env|

Check out our other articles on Rack::Rewrite

Rack::Rewrite 1.0.0 Released
Rack::Rewrite 0.2.1 Released
Rack::Rewrite + Google Analytics Makes Site Transitions Seamless

  Follow @smartlogic