Front Controller Pattern in Java, Python and PHP
Route all incoming requests through a single handler that dispatches to the appropriate page command, centralizing request processing and security.
Overview
The Front Controller pattern routes every incoming request through a single entry point — one central handler that runs shared logic (authentication, logging, input normalization) and then dispatches to the command or controller responsible for the page. Instead of each page handling its own request parsing, all of that cross-cutting work lives in one place.
This is the pattern behind virtually every modern web framework. Spring’s DispatcherServlet, the index.php bootstrap in Symfony and Laravel, Django’s WSGI handler, and Express’s middleware pipeline are all Front Controllers. One component receives the request, preprocesses it, and delegates to the code that actually produces the page.
The Problem It Solves
In the classic PHP-era model, every URL mapped to a file — home.php, user.php, admin.php — and each file had to repeat the same bootstrap: connect to the database, start the session, check auth, set the locale. Pages drifted apart; a fix to the auth check meant editing twenty files, and a missed one was a security hole.
Front Controller inverts that. The web server sends everything to one file, and that file owns the shared logic before deciding which page code to run. The pattern was formalized in the early J2EE world (Sun’s Core J2EE Patterns, later Fowler’s P of EAA) as the answer to duplicated request handling, and it’s why frameworks made rewriting to a single entry point a convention rather than a choice.
When to Use
Use the Front Controller pattern when:
- Several pages share the same preprocessing — authentication, logging, encoding.
- You want clean URL routing decoupled from the file structure.
- Request handling must be consistent across the whole application.
- Centralized security checks and error handling matter more than a bit of extra indirection.
When to Avoid
- Very small sites with a handful of pages — the indirection buys you nothing.
- Static sites where each page can be served directly.
- When the controller starts knowing about page logic — that’s a God object, not a Front Controller.
Solution
The canonical form (Fowler’s Patterns of Enterprise Application Architecture, Oracle’s Core J2EE Patterns) splits the work into three roles: the front controller that receives the request, commands that do the page-specific work, and a dispatcher that selects the view. Modern frameworks collapse these into a router plus middleware, which is the shape the examples follow here.
Java — servlet and command objects
The classic J2EE form: one servlet gets every request, looks up a Command, and the command decides what to render.
// File: Command.java
public interface Command {
String process(HttpServletRequest req) throws ServletException;
}
// File: HomeCommand.java
public class HomeCommand implements Command {
public String process(HttpServletRequest req) {
return "/WEB-INF/views/home.jsp";
}
}
// File: FrontControllerServlet.java — mapped to /* in web.xml or @WebServlet("/*")
@WebServlet("/*")
public class FrontControllerServlet extends HttpServlet {
private final Map<String, Command> commands = new HashMap<>();
@Override
public void init() {
commands.put("/", new HomeCommand());
commands.put("/user", new UserCommand());
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Shared preprocessing for every request
if (!isAuthenticated(req)) {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
Command command = commands.get(req.getPathInfo());
if (command == null) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Dispatch: the command returns the view to render
String view = command.process(req);
req.getRequestDispatcher(view).forward(req, resp);
}
private boolean isAuthenticated(HttpServletRequest req) {
return "Bearer valid".equals(req.getHeader("Authorization"));
}
}
PHP — the index.php bootstrap
Every PHP framework routes all traffic through one file. The web server rewrites every URL to index.php, which boots the app, runs middleware-equivalent code, and dispatches:
<?php
// index.php — every request lands here
declare(strict_types=1);
$routes = [
'/' => HomeController::class,
'/user' => UserController::class,
];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// Shared preprocessing: auth check before any controller runs
if (($_SERVER['HTTP_AUTHORIZATION'] ?? '') !== 'Bearer valid') {
http_response_code(401);
exit('Unauthorized');
}
$controllerClass = $routes[$path] ?? null;
if ($controllerClass === null) {
http_response_code(404);
exit('Not Found');
}
$response = (new $controllerClass())->handle();
echo $response;
JavaScript — Express as a front controller
Express’s app object is a front controller: app.use() registers preprocessing steps, app.get()/app.post() map routes to handlers.
const express = require('express');
const app = express();
// Shared preprocessing — every request passes through here
app.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
});
app.use((req, res, next) => {
if (req.headers.authorization !== 'Bearer valid') {
return res.status(401).send('Unauthorized');
}
next();
});
// Route handlers — the "commands"
app.get('/', (req, res) => res.send('Welcome to the home page'));
app.get('/user', (req, res) => res.send('User profile page'));
// Fallback dispatch
app.use((req, res) => res.status(404).send('Not Found'));
app.listen(3000);
Explanation
Three roles make the pattern work:
- Front controller — the single entry point. Parses the request, runs authentication and validation, and owns error handling.
- Commands — the page-specific logic. Each command handles one route or action and returns the data or view to render.
- Dispatcher — maps the request to a command and hands the result to the view. In simple implementations the controller dispatches itself; frameworks split it out (Spring’s
HandlerMapping+ViewResolver).
Authentication and input filters often run before the front controller in their own chain — that’s the companion Intercepting Filter pattern. And when page controllers each render their own view, the front controller plus commands together form the “C” of MVC: the controller layer, not the whole architecture.
Edge cases worth knowing
- WebSocket upgrades bypass normal dispatch — inspect the
Upgradeheader early and route them to a dedicated handler. - REST APIs usually skip the view step entirely; the “dispatch” writes JSON directly.
- Async flows (jobs, callbacks) need manual context propagation — the request/response lifecycle ends before they run.
Variants
| Variant | Mechanism | Use Case |
|---|---|---|
| Servlet filter chain | Filters run before the controller | Authentication, encoding, compression |
| Middleware stack | Express / Django middleware | Cross-cutting request processing |
| Dispatcher servlet | Spring DispatcherServlet | Full MVC with view resolution |
| Reverse proxy | Nginx / Apache at the edge | Load balancing, TLS termination, caching |
What Works
- Keep the controller lean. It dispatches; it doesn’t implement page logic.
- Use a declarative route table. URL-to-command maps beat nested if-else chains.
- Run shared concerns first. Authentication, CSRF protection, and input sanitization belong in the entry point.
- Support interceptors. Let middleware modify requests and responses without touching the core.
- Fail fast. Reject invalid requests before they reach page logic.
Common Mistakes
- Bloated front controller that knows about page internals. If it’s carrying business logic, split it into commands.
- Tight coupling to one view technology. The controller shouldn’t generate HTML.
- Ignoring HTTP semantics. Returning 200 for every response hides failures from clients and crawlers.
- No error boundary. Unhandled exceptions leak stack traces — wrap dispatch in a single catch-all.
- Blocking work inline. Long-running operations belong behind the controller, in a queue.
Real-World Examples
Spring MVC
DispatcherServlet is the front controller: it receives every HTTP request, resolves the handler through HandlerMapping, runs interceptors, and delegates rendering to ViewResolver. The pattern ships built in — you register controllers, Spring does the dispatch.
Laravel / Symfony
Both PHP frameworks funnel every request through public/index.php, which boots the kernel, runs middleware, and hands off to the router. Apache/nginx rewrite rules make the single entry point invisible to the user.
Express.js
app.use() chains plus app.METHOD() routes are the pattern worn openly: middleware is the shared preprocessing, routes are the commands, and the final error-handling middleware is the dispatcher’s safety net.
Further Reading
- Front Controller — Martin Fowler’s P of EAA catalog — the canonical definition and its command-based structure.
- Core J2EE Patterns: Front Controller — Oracle — the original Java EE treatment with dispatcher and view management.
- Intercepting Filter pattern — the companion pattern for pre/post-processing.
- Front controller — Wikipedia — history and framework survey.
Frequently Asked Questions
What is the difference between Front Controller and Page Controller?
Page Controller uses one controller per page — each page handles its own request. Front Controller puts a single entry point in front of all of them, which centralizes shared logic but adds one hop of indirection.
Is Front Controller the same as MVC?
No. Front Controller is one way to organize the C in MVC. MVC also defines the Model and View; a front controller only covers how requests reach controllers.
Do modern frameworks still use this pattern?
Yes — most of them are built on it. Spring's DispatcherServlet, Laravel's index.php, Django's WSGI handler, and Express's middleware pipeline are all front controllers with extra machinery around them.
Can a front controller handle WebSocket connections?
It can, as long as it inspects the Upgrade header early and hands the connection to a dedicated handler before HTTP dispatch runs. Most frameworks treat WebSockets as a separate pipeline for this reason.
When is a front controller overkill?
On a site with a handful of static pages, or a tiny API with two routes. The pattern pays off when several endpoints share preprocessing — if there's nothing to share, there's nothing to centralize.
Related Resources
Page Controller Pattern
Use a dedicated controller object for each logical page in a web application, handling the request and populating the view for that specific page.
PatternIntercepting Filter: Pluggable Request Pipelines
Compose cross-cutting concerns into a chain of pluggable filters that intercept requests and responses, enabling reusable preprocessing and postprocessing logic.
PatternMVC Pattern: Model, View, Controller Explained
Separate an application into Model, View, and Controller layers. When MVC pays off, when it's over-engineering, and how the pieces interact.
PatternGatekeeper Pattern: Centralized Edge Security
Centralize authentication, rate limiting, and input sanitization at the system edge. Gatekeeper Pattern with Python, Java, and JavaScript examples.
PatternModel-View-Presenter (MVP) Pattern
Separate presentation logic from the view by introducing a presenter that intermediates between the model and a passive view, enabling testable UI code.