SmartLogic Logo (443) 451-3001

The SmartLogic Blog

SmartLogic is a web and mobile product development studio based in Baltimore. Contact us for help building your product or visit our website to learn more about what we do.

Rack::Rewrite for Site Maintenance and Downtime

November 16th, 2009 by

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|
    File.exists?(maintenance_file)
  }

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|
    File.exists?(maintenance_file)
  }
  • http://technovangelism.com/blog/ Erik

    Very cool. This definitely makes it a lot easier to switch between Apache and Nginx.

John Trupiano co-founded SmartLogic with Yair Flicker in May 2005 and was co-president through 2011. Check out his GitHub Projects or follow @jtrupiano on Twitter.

John Trupiano's posts