Course topics

By WebNest Studio Engineering Team

Advanced Java Tutorial

RequestDispatcher: Forward vs Include

Servlets frequently need to hand off processing to another resource — often a JSP that renders the final HTML, or another servlet that handles a sub-task. Java provides three distinct mechanisms for this, and mixing them up is one of the most common sources of confusing bugs in servlet-based applications: RequestDispatcher.forward(), RequestDispatcher.include(), and HttpServletResponse.sendRedirect().

The key distinction is server-side vs client-side. Forward and include both happen entirely on the server, within the same request; sendRedirect tells the browser to make an entirely new, separate request.

forward() — Hand Off Control, Same Request

RequestDispatcher.forward(request, response) transfers control to another resource on the server, within the same request and response objects. Any attributes set with request.setAttribute() before the forward are still visible to the target resource — this is the standard way a controller servlet passes data to a JSP view. The browser's address bar does not change, because the browser is never told a redirection happened; it only ever sees the response from the final resource. Anything written to the response before the forward should generally be avoided, since forward is meant to hand off rendering, not append to it.

include() — Embed Another Resource's Output

RequestDispatcher.include(request, response) executes another resource and inserts its output into the current response, then returns control back to the original servlet, which can continue writing more content afterward. This is useful for composing a page out of reusable fragments — a header, a footer, or a sidebar — much like a template include. Unlike forward, the including resource retains control and can write both before and after the include call.

sendRedirect() — A New Request From the Browser

response.sendRedirect(url) sends an HTTP 302 (or 303) status back to the browser with a Location header; the browser then issues a brand-new GET request to that URL. This is client-side: the address bar changes to the new URL, request attributes from the original request are lost (because it truly is a new request), and it works for redirecting to external sites, unlike forward/include which only work within the same application's resources.

  • forward() — server-side, same request, URL unchanged, attributes preserved.
  • include() — server-side, embeds output, control returns to caller.
  • sendRedirect() — client-side, new request, URL changes, attributes lost, can target external URLs.

Examples

Controller servlet forwarding to a JSP view with request attributes

Java
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ProductServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setAttribute("productName", "Wireless Mouse");
        request.setAttribute("price", 799.00);

        // Server-side hand-off: URL stays "/products", attributes are visible in the JSP
        RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views/product.jsp");
        dispatcher.forward(request, response);
    }
}
Output
Browser address bar remains: /products
Rendered page (from product.jsp using ${productName} and ${price}):
Product: Wireless Mouse
Price: 799.0

sendRedirect vs forward — the address bar difference

Java
// Option A: forward — address bar stays "/checkout"
request.getRequestDispatcher("/WEB-INF/views/confirmation.jsp").forward(request, response);

// Option B: sendRedirect — address bar changes to "/confirmation", new GET request issued
response.sendRedirect("confirmation");
Output
Forward: bar shows /checkout while confirmation.jsp renders. Redirect: browser navigates to /confirmation with a fresh request; refreshing the page does not resubmit the original form data.

Common Mistakes

  • Using sendRedirect() after a form POST and expecting request.getAttribute() values to still be available in the next page — a redirect starts a brand-new request.
  • Calling forward() after the response has already been committed (partially written and flushed), which throws an IllegalStateException.
  • Using forward() when an external URL is intended — forward only works for resources within the same web application.
  • Confusing include() with forward() and being surprised that the including servlet can still write to the response afterward.

Exercise

Using the lesson's Controller servlet forwarding to a JSP view with request attributes example, create a small implementation or design sketch that demonstrates forward() and include() are server-side and share the same request/response; sendRedirect() is client-side and creates a new request.. Add a test case or scenario that would catch Using sendRedirect() after a form POST and expecting request.getAttribute() values to still be available in the next page — a redirect starts a brand-new request., and describe the expected result.

Show hints
  • Start with forward() — Hand Off Control, Same Request and compare your approach with Controller servlet forwarding to a JSP view with request attributes. Identify the decision or behavior the lesson recommends.
  • Walk through your scenario step by step. Confirm that it follows forward() and include() are server-side and share the same request/response; sendRedirect() is client-side and creates a new request. and that it would catch the failure described by Using sendRedirect() after a form POST and expecting request.getAttribute() values to still be available in the next page — a redirect starts a brand-new request..

Quick quiz

1. Which choice is a mistake to avoid in RequestDispatcher: Forward vs Include?

  • A. forward() and include() are server-side and share the same request/response; sendRedirect() is client-side and creates a new request.
  • B. Using sendRedirect() after a form POST and expecting request.getAttribute() values to still be available in the next page — a redirect starts a brand-new request.
  • C. Calling forward() after the response has already been committed (partially written and flushed), which throws an IllegalStateException.
Show answer

B. Avoid this approach: Using sendRedirect() after a form POST and expecting request.getAttribute() values to still be available in the next page — a redirect starts a brand-new request. The lesson recommends: forward() and include() are server-side and share the same request/response; sendRedirect() is client-side and creates a new request.

Interview questions

How would you apply the guidance in forward() — Hand Off Control, Same Request to a real task, and what failure would you guard against?

Show answer

forward() and include() are server-side and share the same request/response; sendRedirect() is client-side and creates a new request. Apply this in forward() — Hand Off Control, Same Request; the example Controller servlet forwarding to a JSP view with request attributes shows one way to do it. Avoid Using sendRedirect() after a form POST and expecting request.getAttribute() values to still be available in the next page — a redirect starts a brand-new request. and explain how your implementation or design prevents that failure.

Key Points to Remember

  • forward() and include() are server-side and share the same request/response; sendRedirect() is client-side and creates a new request.
  • forward() hands off rendering entirely; include() embeds another resource's output and returns control to the caller.
  • Only sendRedirect() changes the browser's address bar and can target URLs outside the current application.
  • Request attributes set before a forward/include survive; they do not survive a redirect.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

These examples need a JDK project with their libraries on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering