Rack middleware to 301 urls with trailing slashes

Language: Ruby

# Rather than serving a resource at both /foo and /foo/, I'd rather just serve it
# at /foo, and return a 301 for /foo/, so that Google won't think I have separate
# pages with the same content, and I won't be keeping duplicate content in caches.
#
# This middleware handles this. Another strategy would be to handle this in Nginx 
# instead of middleware.
module Rack
  class TrailingSlash

    def initialize(app)
      @app = app
    end

    def call(env)
      if env['PATH_INFO'] =~ %r{^/(.*)/$}
        location = "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}/#{$1}"
        location = "#{location}?#{env['QUERY_STRING']}" if env['QUERY_STRING'].to_s =~ /\S/
        [301, {"Location" => location}, []]
      else
        @app.call(env)
      end
    end

  end
end
Reveal More
Added over 2 years ago by Devo_normal gbuesing

Refactorings

Re: Rack middleware to 301 urls with trailing slashes

Refactoring of: Rack middleware to 301 urls with trailing slashes

Language: Ruby

# Modified from the original to store the regexp in a constant, to simplify 
# the removable of the trailing slash, and to utilize Rack to rebuild the URL.

module Rack
  class TrailingSlash

    HAS_TRAILING_SLASH = %r{^/(.*)/$}

    def initialize(app)
      @app = app
    end

    def call(env)
      if env['PATH_INFO'] =~ HAS_TRAILING_SLASH
        env['PATH_INFO'].chomp!('/')
        [301, {"Location" => Rack::Request.new(env).url}, []]
      else
        @app.call(env)
      end
    end

  end
end

# NOTE: real-world experience shows this may not work if used behind Varnish, 
# because the port will be changed due to the proxy_pass...
Reveal More
Added over 2 years ago by Costco_normal trevorturk