Question
In Ruby on Rails, how can I return a 404 "Not Found" response to the client?
In PHP, this might be done by manually sending an HTTP header such as:
header("HTTP/1.0 404 Not Found");
What is the Rails way to do the same thing, especially when I want to show a not found page or indicate that a resource does not exist?
Short Answer
By the end of this page, you will understand how Rails sends HTTP 404 responses, how to render a custom not found page, when to raise an exception versus rendering directly, and which patterns are commonly used in real Rails applications.
Concept
In web applications, a 404 Not Found response means the server was reached successfully, but the requested resource does not exist.
In Rails, you usually do not manually write raw HTTP headers the way you might in PHP. Instead, Rails provides higher-level methods for building a response:
renderto show a template or fileheadto send only status code and headers- exceptions such as
ActiveRecord::RecordNotFoundthat Rails can convert into a 404 response
This matters because Rails follows the conventions of the HTTP request/response cycle. Rather than thinking "send a header string," think in terms of:
- What content should the user see?
- What HTTP status should be returned?
A 404 is important for both users and machines:
- Users see that the page does not exist
- Browsers and API clients understand the response correctly
- Search engines know the page is missing instead of thinking it loaded successfully
A common beginner mistake is to render a page that says "Not Found" but forget to set the status to 404. In that case, the browser receives a 200 OK, which is misleading.
In Rails, common ways to return a 404 include:
head :not_found
or:
render file: Rails.public_path.join("404.html"), status: :not_found
Mental Model
Think of a Rails controller action like a receptionist at a building.
- If the visitor asks for a person who works there, the receptionist sends them to the right room.
- If the person does not exist, the receptionist says, "That person is not here," and marks the request as not found.
In PHP, you might manually write the receptionist's note yourself. In Rails, you usually hand Rails a structured instruction such as:
- "Send no body, just say not found"
- "Show this 404 page and mark it as not found"
- "This record doesn't exist; treat it as not found"
So instead of manually constructing the raw HTTP message, you tell Rails the meaning of the response, and Rails builds the correct HTTP output for you.
Syntax and Examples
Core syntax
1. Return only a 404 status
head :not_found
Use this when you do not need to render a page body, such as in a small API endpoint.
2. Render a page with 404 status
render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
Use this when you want to show a static error page.
3. Render custom text or JSON with 404 status
render plain: "Not Found", status: :not_found
render json: { error: "Not Found" }, status: :not_found
This is useful for APIs or simple responses.
Beginner-friendly example
Imagine you are looking up an article by ID:
class ArticlesController <
article = .find_by( params[])
article.?
render .public_path.join(), ,
render
Step by Step Execution
Consider this controller action:
class ProductsController < ApplicationController
def show
product = Product.find_by(id: params[:id])
if product.nil?
render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
return
end
render plain: product.name
end
end
Now imagine the request is:
GET /products/999
and product 999 does not exist.
Step by step
-
Rails routes the request to
ProductsController#show. -
params[:id]contains"999". -
runs a database query.
Real World Use Cases
404 responses are used in many practical situations:
Missing database records
A user visits:
/articles/123
but article 123 has been deleted.
Hidden or unavailable content
Sometimes an app intentionally returns 404 instead of revealing whether a resource exists, for example:
- private documents
- unpublished posts
- disabled user profiles
API endpoints
An API may return:
{ "error": "User not found" }
with status 404 when a requested user ID does not exist.
Invalid nested routes
Example:
/users/5/orders/999
If order 999 does not belong to user 5, the app may return 404.
Old or removed pages
When content has been removed and no replacement exists, 404 is the correct status.
This helps search engines and clients understand the page is unavailable.
Real Codebase Usage
In real Rails projects, developers usually use a few common patterns.
1. Let find raise ActiveRecord::RecordNotFound
def show
@article = Article.find(params[:id])
end
This is common because it is short and expressive. Missing records are treated as exceptional cases.
2. Rescue missing-record exceptions in one place
Many apps handle 404 errors centrally in ApplicationController:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
private
def render_not_found
render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
end
end
Common Mistakes
1. Showing a 404 message with a 200 status
Broken example:
render plain: "404 Not Found"
Problem:
- The text says 404
- The actual HTTP status is still
200 OK
Fix:
render plain: "404 Not Found", status: :not_found
2. Using redirect_to for a 404
Broken idea:
redirect_to "/404"
Problem:
- A redirect sends a
3xxresponse first, not a 404 - It changes the URL instead of reporting that the requested resource was missing
Use render or head instead.
3. Forgetting to stop execution after rendering
Broken example:
def
something_missing?
render .public_path.join(), ,
render
Comparisons
| Approach | What it does | Best for | Notes |
|---|---|---|---|
head :not_found | Sends only a 404 status, no page body | APIs, lightweight responses | Very simple |
render ..., status: :not_found | Sends content plus 404 status | HTML pages, JSON errors, text responses | Most flexible |
raise ActiveRecord::RecordNotFound | Signals that a record is missing | Record lookups with find | Rails can handle this automatically |
redirect_to | Sends a redirect response | Moving users to another page | Not a true 404 |
find vs
Cheat Sheet
# Status only
head :not_found
# Render static 404 page
render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
# Render text with 404
render plain: "Not Found", status: :not_found
# Render JSON with 404
render json: { error: "Not Found" }, status: :not_found
# Raise a missing-record exception
raise ActiveRecord::RecordNotFound
# Common Active Record lookup
@post = Post.find(params[:id]) # raises if missing
@post = Post.find_by(id: params[:id]) # returns nil if missing
Quick rules
- Use
status: :not_foundfor a real 404 response - Use
head :not_foundwhen no response body is needed
FAQ
How do I return a 404 in Rails?
Use one of these common approaches:
head :not_found
or:
render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
How do I show the Rails 404 page?
Render the static file from public/404.html with status :not_found:
render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
Is redirect_to the right way to send a 404?
No. redirect_to sends a redirect status like 302, not a 404. Use render or head instead.
Mini Project
Description
Build a small Rails controller that serves article pages and correctly returns a 404 response when an article does not exist. This demonstrates the difference between a successful response and a proper not found response, which is a common requirement in real applications.
Goal
Create a controller action that finds an article by ID and returns either the article content or a proper 404 response.
Requirements
- Create an
ArticlesControllerwith ashowaction. - Look up an article using the ID from
params[:id]. - If the article does not exist, return a 404 response.
- If the article exists, show its title.
- Use Rails conventions rather than manually setting raw HTTP headers.
Keep learning
Related questions
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.
Difference Between require and include in Ruby
Learn the difference between require and include in Ruby, when to load a file, and when to mix module methods into a class.
Fixing Ruby Gem Native Extension Errors: mkmf.rb Can't Find Header Files for Ruby
Learn why Ruby gem installs fail with missing ruby.h, how native extensions work, and how to fix header file errors on Linux servers.