I found this great blog post on how to use Rack::Proxy
as a separate proxy app. The article explains how he uses Rack::Proxy
to proxy requests to http://localhost:3000
to an app on port 3001
and requests to http://localhost:3000/api
to an app on port 3002
. I want to do the same thing, but I do not want to create a separate proxy app. Instead, I want my main Rails app to proxy requests to /blog
to a different app.

- 9,254
- 12
- 54
- 75
5 Answers
FWIW, I also just tackled this problem. Some may find the full code helpful, as I needed more than you posted:
# lib/proxy_to_other.rb
class ProxyToOther < Rack::Proxy
def initialize(app)
@app = app
end
def call(env)
original_host = env["HTTP_HOST"]
rewrite_env(env)
if env["HTTP_HOST"] != original_host
perform_request(env)
else
# just regular
@app.call(env)
end
end
def rewrite_env(env)
request = Rack::Request.new(env)
if request.path =~ %r{^/prefix|^/other_prefix}
# do nothing
else
env["HTTP_HOST"] = "localhost:3000"
end
env
end
end
Also:
# config/application.rb
# ...snip ...
module MyApplication
class Application < Rails::Application
# Custom Rack middlewares
config.middleware.use "ProxyToOther" if ["development", "test"].include? Rails.env
#...snip....
This assumes your app you want to proxy some requests to is running on port 3001. I daresay the app you're hitting can be run on any port. This also assumes you only want to do the proxying in development and test environments, because you'll have a 'real' solution in production & staging (eg, nginx or a loadbalancer doing the proper thing).

- 3,276
- 27
- 25
-
2This is great. If you want to proxy to an arbitrary app on the internet, you may also have to set `env["SERVER_PORT"] = 80` – Gabe Kopley Jun 26 '13 at 22:45
-
" This also assumes you only want to do the proxying in development and test environments, because you'll have a 'real' solution in production & staging (eg, nginx or a loadbalancer doing the proper thing)." - Why is that? Shouldn't I use Rack::Proxy in production? – David Lojudice Sb. Jan 07 '14 at 14:17
-
2@DavidLojudiceSobrinho you certainly could, but it would be better to do that proxying with nginx/apache or a loadbalancer. You'll be using up a thread in your app to do a synchronous HTTP request to some other server. It's just not a wise use of resources. – steve Jan 09 '14 at 19:16
-
1Great answer, @steve. Sorry for not putting any detail into my original answer. It has been some now since I encountered that problem and I don't even have the code base for it anymore. Looks like wbyoung also has a good answer, so to save an argument, I'll give you guys both an upvote. – John Mar 26 '14 at 05:33
-
How do I write a test for this in Rspec? – Yoni Baciu Aug 05 '14 at 21:48
-
1@YoniBaciu if you wanted to write a unit spec, you would instantiate an instance of this and call its methods and then make assertions. Assuming you're using webmock, you could assert that a request to your other service was made. – steve Aug 07 '14 at 07:59
This is a slight change to steve's solution that uses a little less internal understanding of Rack::Proxy
:
require 'rack/proxy'
class MyProxy < Rack::Proxy
def initialize(app)
@app = app
end
def call(env)
# call super if we want to proxy, otherwise just handle regularly via call
(proxy?(env) && super) || @app.call(env)
end
def proxy?(env)
# do not alter env here, but return true if you want to proxy for this request.
return true
end
def rewrite_env(env)
# change the env here
env["HTTP_HOST"] = "some.other.host"
env
end
end

- 22,383
- 2
- 34
- 40
Figured it out.
lib/proxy.rb
require 'rack-proxy'
class Proxy < Rack::Proxy
def initialize(app)
@app = app
end
def rewrite_env(env)
# do magic in here
end
end
config/application.rb
config.middleware.use "Proxy"

- 4,571
- 1
- 21
- 32

- 9,254
- 12
- 54
- 75
-
26would have been nice to see what exactly the "magic" was that you were talking about – steve Nov 24 '12 at 06:26
-
1
Below is even simpler code to proxy an api when you want http://localhost:3000/api/users/1 (for example) to go to the api namespace defined in routes.rb without using a proxy server program.
On production it would be something like http://api.sample.com/users/1.
lib/proxy.rb
require 'rack-proxy'
class Proxy < Rack::Proxy
def perform_request(env)
request = Rack::Request.new(env)
if request.path =~ %r{^/api}
#do nothing
else
@app.call(env)
end
end
end
config/application.rb
config.middleware.use "Proxy"
config/routes.rb
namespace :api, defaults: { format: :json },
constraints: { subdomain: 'api' }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users, :only => [:show, :create, :update, :destroy]
end
lib/api_constraints.rb
class ApiConstraints
def initialize(options)
@version = options[:version]
@default = options[:default]
end
def matches?(req)
@default || req.headers['Accept'].include?("application/vnd.sample.v#{@version}")
end
end

- 300
- 2
- 12
I found gem rails-reverse-proxy more simpler and obvious to use (for react app):
Add a simple proxy controller:
class ProxyController < ApplicationController
include ReverseProxy::Controller
def index
reverse_proxy "http://localhost:3000" do |config|
# We got a 404!
config.on_missing do |code, response|
redirect_to root_url and return
end
end
end
end
And add a route:
match 'static/*path' => 'proxy#index', via: [:get, :post, :put, :patch, :delete]

- 7,935
- 2
- 26
- 42