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
end
Now 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
end
The 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
end
Now, 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
end
Let'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
end
So 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
end
In 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] # 22
To 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
end
Retrieving 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.com
Retrieving 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)
end
The 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
end
To access the request's headers, remember to use request.headers
as follows:
class ProjectsController < ApplicationController
def rails
http_method = request.headers["REQUEST_METHOD"]
end
end
There 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
end
Finally, 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
)
end
However, 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_post
raw_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
end
You 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.