HTTP is a request-response based protocol. The browser (acting as the client) sends an HTTP request to the server (your Rails application). The server then parses the request, processes it, and generates an HTTP response, which is sent back to the browser. Each request carries a bunch of useful information that your application frequently relies on to handle the request properly.
Rails offers a simple and straightforward interface to work with the current HTTP request hitting your application. It also exposes a bunch of super useful methods to access details like request data, including input parameters, URLs, paths, and the request body that comes with each request.
In this post, we’ll explore some of the most commonly used methods for inspecting incoming HTTP requests. First, let’s take a look at how you can access the request object inside a controller class.
Accessing the Request
To obtain an instance of the current HTTP request in your controller class, use the request method, which returns an instance of the ActionDispatch::Request class.
class RequestsController < ApplicationController
def show
puts request # #<ActionDispatch::Request:0x0000000128d38c08>
end
endNow you might be wondering where this request method (is it even a method?) is defined in the Rails controller hierarchy. So let's inspect the Rails codebase, specifically the ActionController::Metal class, which is the great-granddaddy of your application controllers.
module ActionController
class Metal
# The ActionDispatch::Request instance for the current request.
attr_internal :request
end
endThe attr_internal method is provided by Rails, not part of standard Ruby. It declares an attribute reader and writer backed by an internally-named instance variable, @_request. This is particularly useful when you want to avoid naming conflicts with public methods or when you want to clearly distinguish internal state variables.
request object is only accessible within controllers — this is intentional as a controller is the correct level of abstraction to handle incoming requests. Just wanted to make that clear, as I often see questions like, “How can I access the request object in my models or service objects?” The short answer is: you don’t. Instead, extract only the necessary data from the request in the controller and pass that data to your models or service objects, instead of trying to access to whole request in there.
The ActionDispatch::Request instance provides a bunch of methods to examine the incoming HTTP request. Let's take a look at some of the important and commonly used methods below.
Retrieving the Request Path
The path method returns the request's path information. If the user visits your application at https://basecamp.com/projects/rails, the path method will return projects/rails.
class ProjectsController < ApplicationController
def rails
url_path = request.path # /projects/rails
end
endNow, if you're like me and your first instinct is to take a look at the Rails API docs every time you come across a new method, you'd be surprised to find that the list of methods listed under the ActionDispatch::Request class doesn't have a path method. So where's that coming from?
As always, let's open the Rails codebase and take a peek at the source code for the ActionDispatch::Request class. Here's the important part:
module ActionDispatch
class Request
include Rack::Request::Helpers
end
endLet's follow the trail and open the Rack::Request::Helpers module located in the Rack gem (run the bundle open rack command). Here's the source code.
module Rack
class Request
module Helpers
def path
script_name + path_info
end
end
end
endSo that's where the path method is coming from. The ActionDispatch::Request class includes the Rack::Request::Helpers module, which provides the path method.
However, the path method only returns the URL path component, and doesn't include any query parameters. If you want the full path, including any query params if present, you'll have to use the fullpath method on the request.
# URL: https://example.com/projects/rails?page=22
class ProjectsController < ApplicationController
def rails
path = request.path # /projects/rails
fullpath = request.fullpath # /projects/rails?page=22
end
endIn practice though, if you need to work with the query params, you'd typically use the params object instead of reading them from the URL. For example, given the same URL endpoint above, you'd access the page query param as follows:
puts params[:page] # 22To learn more about Rails parameters, check out my previous post in the series on Rails controllers:

Retrieving the Request URL
To access the complete URL including the domain for the incoming request, you can use the url method.
class ProjectsController < ApplicationController
def rails
url = request.url # http://www.example.com/projects/rails?page=22
path = request.path # /projects/rails
end
endRetrieving the Request Host
If you just want the domain name, you can retrieve the host of the incoming request via the host method.
puts request.host # www.example.comRetrieving the Request Method
The method method returns the HTTP verb for the request.
request.method # "GET"The nice thing about this method is that it will return the original value of the request method, even if it was overridden by any middleware.
Working with Headers
The headers method on ActionDispatch::Request provides access to the request's HTTP headers.
request.headers["Content-Type"] # "text/plain"Here's the implementation of this method:
# actionpack/lib/action_dispatch/http/request.rb
def headers
@headers ||= Http::Headers.new(self)
endThe Http::Headers provides a hash-like storage, giving you all the headers associated with this request. You can get and set a value of a particular header just like you get and set values for a hash.
Important: If you just call the headers method in a controller, it will give you the response headers, i.e. headers sent with the response.
class ProjectsController < ApplicationController
def rails
headers == response.headers # true
end
endTo access the request's headers, remember to use request.headers as follows:
class ProjectsController < ApplicationController
def rails
http_method = request.headers["REQUEST_METHOD"]
end
endThere is one gotcha when accessing headers like this. If a particular header doesn't exist, Rails will return nil, which may not what you want. Sometimes, you want to know if the header is indeed not present, and use a default value if it's missing. For this, use the fetch method on the Headers class.
It accomplishes two things: When the header is not present, it will raise a KeyError. Alternatively, you can pass an optional second argument that will be returned if the headers is not present on the request.
class ProjectsController < ApplicationController
def rails
request.headers.fetch("X_REQUEST_METHOD") # KeyError: X_REQUEST_METHOD
request.headers.fetch("X_REQUEST_METHOD", "GET") # "GET"
end
endFinally, you can use the key? or include? methods to check if a particular header value is present or not, just like a regular Hash.
Request IP Address
You can use the ip method to retrieve the IP address of the user (or client) that make the HTTP request to your Rails application.
ip_address = request.ip # "127.0.0.1"In addition, Rails also provides a remote_ip method on the request object. In development (or often in production), you will see the same value for both.
remote_ip_address = request.remote_ip # "127.0.0.1"So, what's the difference between the ip and remote_ip methods?
Sometimes, the client doesn't directly send the request to you, but via intermediate proxies, each of which forwards it to the next proxy before the request reaches your application. In these cases, the server would only see the final proxy's IP address, which is not that useful, as it's often a load balancer.
To solve this, each proxy is supposed to set the X-Forwarded-For header. This header identifies the originating IP address of a client connecting to a server via a proxy server.
X-Forwarded-For: <client>, <proxy>, …, <proxyN>When a request goes through multiple proxies, the IP addresses of each successive proxy are listed. The rightmost IP address is the IP address of the most recent proxy and the leftmost IP address is the address of the originating client.
Rails provides a RemoteIp middleware which calculates the IP address of the remote client making the request, and exposes it via remote_ip method on the request.
Accessing Request Body
If you are building an API or any sort of web services, you often want to work with the raw, unchanged request body. For a long time, I used to add this helper method in my Rails controllers to access the incoming request body.
def request_body
@request_body ||= (
request.body.rewind
request.body.read
)
endHowever, Rails provides an awesome raw_post method which does it for you. In your Rails controller, you can access this method as follows:
request.raw_postraw_post method was introduced by Toby Lütke, the founder of Shopify.Unique Request ID
In Rails, each request gets its own unique identifier, useful for end-to-end tracing of a request, logging, and even for debugging. This is set by the ActionDispatch::RequestId middleware and you can access it using the request_id method on the request.
class ProjectsController < ApplicationController
def rails
id = request.request_id # "1659c4b4-1557-43dc-8883-f03968888f32"
end
endYou can also use request.uuid which is an alias of the above method.
Summary
Here's a quick glance at most of the methods available on the request object.
require "test_helper"
class ProjectsControllerTest < ActionDispatch::IntegrationTest
test "methods on the request object" do
post projects_publish_path(tag: "programming"), params: { title: "Ruby on Rails", description: "A web framework for Ruby" }, headers: { "X-Project-Id" => "58432" }
assert_equal "title=Ruby+on+Rails&description=A+web+framework+for+Ruby", request.raw_post
assert_equal 56, request.content_length
assert_equal "/projects/publish", request.path
assert_equal "/projects/publish?tag=programming", request.fullpath
assert_equal "58432", request.headers["X-Project-Id"]
assert_equal "127.0.0.1", request.ip
assert request.local?
assert_equal "application/x-www-form-urlencoded", request.media_type
assert_equal "POST", request.method
assert_equal :post, request.method_symbol
assert_equal {"tag"=>"programming"}, request.query_parameters
assert request.request_id
assert_equal {"title"=>"Ruby on Rails", "description"=>"A web framework for Ruby", "tag"=>"programming", "controller"=>"projects", "action"=>"publish"}, request.parameters
end
end
Hope that helps.
That's a wrap. I hope you found this article helpful and you learned something new.
As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I reply to all emails I get from developers, and I look forward to hearing from you.
If you'd like to receive future articles directly in your email, please subscribe to my blog. Your email is respected, never shared, rented, sold or spammed. If you're already a subscriber, thank you.
