Routing
Routing is the layer that decides which controller and method handles an incoming URL. In CodeIgniter 4,
almost all of that decision-making lives in one file, app/Config/Routes.php, though the
framework also offers a convention-based fallback for when you haven't defined a route at all. This page
covers both approaches, the placeholder syntax used to capture URL segments, route groups, and named routes.
1. app/Config/Routes.php: the Single Source of Truth
Every explicit route in a CodeIgniter application is registered in app/Config/Routes.php. This
file is loaded once per request, and it exposes a $routes object with methods for each HTTP
verb. A minimal file looks like this:
<?php
$routes->get('/', 'Home::index');
$routes->get('articles', 'Articles::index');
The first argument is the URL pattern to match (relative to your base URL), and the second is a string in
Controller::method form telling CodeIgniter which class and action to call.
2. Automatic (Default) Routing
If a URL doesn't match any route you've defined, CodeIgniter falls back to its automatic routing convention rather than immediately returning a 404. Under this convention, the first URL segment maps to a controller class, the second to a method on that class, and any further segments become arguments passed to that method — positionally, in order.
For example, with zero routes explicitly defined for it, a request to /articles/view/5 is
automatically routed to:
App\Controllers\Articles::view(5)
This convention will feel familiar if you've used other MVC frameworks with similar "convention over configuration" defaults. It's convenient for quick prototyping, but most real applications either don't rely on it or actively disable it, because it means any public method on any controller becomes reachable by URL unless you're careful.
$routes->setAutoRoute(false); in Routes.php. Once disabled, a URL only works if
it matches an explicit route you've defined — nothing is reachable by convention alone. Most
production applications enable this for the extra control and security.3. Defining Explicit Routes
Explicit routes give you full control: a custom URL pattern, a specific controller/method target, and
(via placeholders) the ability to capture parts of the URL as arguments. Here's a route that captures a
numeric article ID and passes it straight through to a show() method:
$routes->get('articles/(:num)', 'Articles::show/$1');
(:num) matches the segment and captures it; $1 in the target refers back to that
first captured group. You can define routes for any HTTP verb your app needs:
$routes->get('articles', 'Articles::index');
$routes->post('articles', 'Articles::create');
$routes->put('articles/(:num)', 'Articles::update/$1');
$routes->delete('articles/(:num)', 'Articles::delete/$1');
4. Route Placeholders
Placeholders are shorthand patterns that stand in for a segment of the URL and control what characters it's allowed to match. The three you'll use most often are:
| Placeholder | Matches | Example URL segment |
|---|---|---|
(:num) | One or more digits only | 5, 1024 |
(:any) | Any character, including additional slashes — greedy, matches the rest of the URL | my/slug/here |
(:segment) | Any character except a forward slash — exactly one URL segment | my-article-slug |
A common mistake is reaching for (:any) when (:segment) is what's actually
wanted — because (:any) will happily swallow extra path segments you didn't intend to
match, it's best reserved for routes where that's genuinely the desired behaviour.
5. Route Groups
When several routes share a common URL prefix, or need the same filters (middleware) applied, grouping
them keeps Routes.php from repeating itself. $routes->group() takes the
shared prefix and a closure containing routes relative to it:
$routes->group('admin', function($routes) {
$routes->get('dashboard', 'Admin\Dashboard::index');
$routes->get('articles', 'Admin\Articles::index');
});
Both routes here are actually reachable at admin/dashboard and admin/articles.
Groups can also take an options array to apply a filter (such as an authentication check) to every route
inside, which is exactly the pattern used to protect an entire admin section in one place — this
ties directly into the filters discussed later in Sessions & Security.
6. Named Routes and Generating URLs
Instead of hardcoding a URL string everywhere it's needed, a route can be given a name. Later, you generate
the actual URL from that name using the route_to() helper (or, in views, the related
url_to() function) — so if the underlying URL pattern ever changes, every place that
generated a link from the route's name keeps working without edits.
$routes->get('articles/(:num)', 'Articles::show/$1', ['as' => 'article.show']);
// Elsewhere in your code, this returns the matching URL, e.g. "articles/5"
route_to('article.show', 5);