HTTP is a request response protocol. The browser (client) sends an HTTP request to the server (Rails application), which parses that request and generates the HTTP response, typically an HTML page, and sends it to the browser. The browser then renders that HTML on the screen.
In the previous post on requests in Rails, we learned how you can work with the request object to access meaningful information on the incoming HTTP request. In this post, we'll take a look at how you can work with the outgoing response. While it’s less common to handle responses directly—since Rails manages most of that for you—there are situations where working with response objects is necessary.
Accessing the Response Object
In a Rails controller, the response
object represents the HTTP response that your application is preparing to send back to the client (browser, API client, etc.). You can access the response in your controllers using the response
object, which is an instance of the ActionDispatch::Response
class.
class ProjectsController < ApplicationController
def show
klass = response.class # => ActionDispatch::Response
code = response.code # => 200
message = response.status_message # => "OK"
# Get and set headers
content_type_options = response.headers["x-content-type-options"]
response.headers["Content-Type"] = "application/json"
end
end
Like the request
attribute, the response
attribute comes from the ActionController::Metal
class. A bunch of common and useful methods are delegated to the response
object.
module ActionController
class Metal
attr_internal_reader :response
delegate :headers, :status, :location, :content_type, :media_type, to: "@_response"
delegate :status=, :location=, :content_type=, to: "@_response"
end
end
Whenever you call any of the above methods, like reading the status, accessing the content_type
, or setting the location
—Rails forwards those calls to the underlying response
object.
attr_internal_reader
declares an attribute reader response
backed by an internally-named instance variable, in our case @_response
.Now let's inspect some of the commonly used methods on the ActionDispatch::Response
object.
What can you do with a Response?
In Rails controllers, the response
object provides several useful methods for inspecting and manipulating the outgoing HTTP response.
In practice, you don't work directly with response
that often. Instead, you use the various helper methods provided by Rails to manipulate the outgoing response. That said, it's good to be aware of the response API, and here're some of the commonly used methods.
body
returns the response body as a string — this is the content that will be sent back to the client. Typically, this is the result of anyrender
call made in the action.
render plain: "Hello, World!"
puts response.body # => "Hello, World!"
body=
sets or overrides the response body manually. This is useful if you want to bypass Rails’ default rendering process and take full control of the response content.
response.body = "Custom response content"
charset
returns the character set (encoding) of the response. HTML wants to know the encoding of the content you’re giving them, so we need to send that along. This is oftenUTF-8
for HTML or JSON responses.
puts response.charset # => "utf-8"
content_type
returns the content type of the response (MIME type). This is set automatically byrender
and other response-generating methods, but you can also inspect or modify it directly.
render json: { message: "Hello" }
puts response.content_type # => "application/json"
content_type=
sets the HTTP response’s content MIME type.
response.content_type = "application/xml"
cookies
returns a Hash of cookies that will be sent with the response, in the form ofname => value
pairs. This can be useful if you need to inspect cookies set during the request lifecycle. We'll explore cookies in detail in a future post.
cookies[:user_id] = 42
puts response.cookies # => { "user_id" => "42" }
location
returns the location of the response, useful when redirecting the response to another location.
redirect_to posts_path
puts response.location # => "http://localhost:3000/posts"
message
returns the standard description for the current HTTP status code. This can be useful for logging or debugging purposes.
response.status = 404
puts response.message # => "Not Found"
send_file
sends a file from the server as the response body. This method is often used when serving downloads like PDFs or images directly.
send_file Rails.root.join("public", "example.pdf")
status=
Sets the HTTP status code.
response.status = 403
to_a
Converts the response into a Rack-compatible array with three elements:[status, headers, body]
. This is useful if you need to work directly at the Rack level (for example, in middleware or low-level response manipulation).
p response.to_a # => [200, { "Content-Type" => "text/html; charset=utf-8" }, ["Rendered page content"]]
Even though the response object exposes all these methods for you to manipulate the outgoing response, you should use them directly only if you have a specific, advanced need to bypass or enhance Rails' built-in helpers.
When to Use Above Methods Directly vs. When to Rely on Rails Helpers
In most Rails controllers, you should prefer higher-level helpers like render
, redirect_to
, head
, and send_file
for setting responses. These methods are designed to work with full response lifecycle in Rails, including content negotiation, layout rendering, error handling, and much more.
# Set body and content type in one step
render json: { message: "Success" }
# Set status and no content
head :no_content
# Redirect to another page - sets the Location header
redirect_to posts_path
Direct use of the response
object is appropriate when:
- You are writing low-level middleware-like logic inside a controller (rare).
- You need to explicitly inspect or override specific response headers (such as adding a custom header for CORS or security purposes).
- You want to debug or log raw response data during development or in a custom logging concern.
- You are building a very customized response flow that does not fit standard rendering or redirection patterns.
In most standard Rails apps, you rarely work directly with response
inside controllers. The response
object is more frequently used in tests where you need to inspect the result of a controller action.
Hope that helps clarify things a little.
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.