Rails Server Proxy
For Ruby idioms see ../integrations/ruby.md.
Routes
ruby
# config/routes.rb
get '/tiles/:z/:x/:y.pbf',
to: 'tiles#show',
constraints: { z: /\d+/, x: /\d+/, y: /\d+/ }Controller
ruby
# app/controllers/tiles_controller.rb
require 'openssl'
require 'http'
class TilesController < ApplicationController
KEY = ENV.fetch('MFD_SRV_KEY')
SECRET = ENV.fetch('MFD_SRV_SECRET')
BASE = 'https://tiles.mapsfordevs.com'
def show
z, x, y = params.values_at(:z, :x, :y)
path = "/tiles/#{z}/#{x}/#{y}.pbf"
ts = Time.now.to_i.to_s
sig = OpenSSL::HMAC.hexdigest('SHA256', SECRET, "GET\n#{path}\n#{ts}")
upstream = HTTP.headers(
'Authorization' => "Bearer #{KEY}",
'X-MFD-Timestamp' => ts,
'X-MFD-Signature' => sig
).timeout(connect: 2, read: 10).get("#{BASE}#{path}")
response.headers['Cache-Control'] = 'public, max-age=86400'
send_data upstream.body.to_s,
type: 'application/x-protobuf',
status: upstream.status,
disposition: 'inline'
rescue HTTP::Error => e
Rails.logger.error("[mfd] tile fetch failed: #{e.message}")
head :bad_gateway
end
endSolid Cache (Rails 8) tile cache
ruby
class TilesController < ApplicationController
def show
cache_key = "mfd:tile:#{params[:z]}:#{params[:x]}:#{params[:y]}"
body = Rails.cache.fetch(cache_key, expires_in: 1.day) do
fetch_upstream
end
response.headers['Cache-Control'] = 'public, max-age=86400'
send_data body, type: 'application/x-protobuf'
end
private
def fetch_upstream
# … same signing + HTTP.get logic
upstream.body.to_s
end
endCORS (rack-cors)
ruby
# config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ENV.fetch('FRONTEND_ORIGIN', 'https://yourapp.com')
resource '/tiles/*', headers: :any, methods: [:get]
end
endAction Controller streaming (faster)
For very high throughput, stream upstream bytes directly instead of buffering:
ruby
class TilesController < ApplicationController
include ActionController::Live
def show
response.headers['Content-Type'] = 'application/x-protobuf'
response.headers['Cache-Control'] = 'public, max-age=86400'
HTTP.headers(sign_headers).get("#{BASE}#{path}").body.each do |chunk|
response.stream.write(chunk)
end
ensure
response.stream.close
end
endProduction deployment
- Run behind nginx / Caddy that handles TLS + HTTP/2.
- Use Puma with
workers * threads ≥ expected concurrency. - Pin
mfd_srv_*source IPs in our dashboard to your egress NAT.