What Are Controller Filters in CodeIgniter 4?
What Are Controller Filters in CodeIgniter 4?
Controller Filters in CodeIgniter 4 are classes that allow you to run code before or after a controller method executes. Unlike global events, which fire framework-wide, filters can be applied selectively to specific URIs, routes, HTTP methods, or even groups of endpoints. This granularity makes them ideal for tasks that need to happen consistently across parts of your application but not everywhere.
Filters operate in two phases:
- Before Filters: These run prior to the controller. They can inspect the incoming request, perform checks (like authentication), and even halt execution by returning a response early.
- After Filters: These execute after the controller has processed the request and generated a response. They are useful for modifying the output, adding headers, or logging actions.
Common use cases include:
- Enforcing CSRF protection on forms.
- Authenticating users for protected routes.
- Implementing rate limiting to prevent abuse.
- Adding security headers to responses.
- Caching pages for performance.
CodeIgniter 4 comes with several built-in filters, such as csrf for cross-site request forgery protection, toolbar for the debug toolbar, and secureheaders for HTTP security headers. But the real power lies in creating custom filters tailored to your needs.
If you are coming from CodeIgniter 3, filters replace the concept of hooks and provide a much cleaner, object-oriented approach. They are conceptually similar to middleware in frameworks like Laravel, but with a CI4-specific implementation that is lightweight and easy to configure.

Table Of Content
1 Prerequisites
Before getting started with this tutorial, make sure you have the following installed on your development machine:
- PHP 8.1 or higher — CodeIgniter 4 requires PHP 8.1 as the minimum version. You can check your version by running php -v in your terminal.
- Composer — The PHP dependency manager is needed to install and manage the CodeIgniter 4 project. Download it from getcomposer.org if you have not already.
- MySQL — While this particular filter example does not require a database, having MySQL ready is recommended as most real-world CI4 projects use it for authentication data storage.
- A code editor — VS Code, PhpStorm, or any editor of your choice.
If you are new to CodeIgniter 4, consider reading our guide on how to build a simple blog in CodeIgniter 4 to get familiar with the framework basics first.
2 Introduction
Introduced as a core component in CodeIgniter 4, filters provide a way to enforce security measures, handle authentication, manage rate limiting, and much more — all without cluttering your controller logic. This guide will walk you through everything you need to know about filters in CodeIgniter 4, from the basics to advanced configurations. We will include practical sample code with detailed explanations to help you implement them in your projects. By the end, you will be equipped to leverage filters for cleaner, more secure code.
In this tutorial, we will build a complete authentication filter that protects admin routes. This is a pattern you will use in almost every CodeIgniter 4 project, whether you are building a role-based login system, a REST API, or a full-featured web application.
Whether you are a beginner upgrading from CodeIgniter 3 or an experienced developer optimizing your CI4 app, understanding filters is essential. Let us dive in.
3 Create / Install a Codeigniter 4 Project
Use the following command to install new Codeigniter Project.
composer create-project codeigniter4/appstarter ci4-filter
Then, navigate to your project directory:
cd ci4-filter
4 Create the Authentication Filter
Here's a step-by-step example of creating a simple authentication filter that checks if a user is logged in before allowing access to certain routes.
Create the Filter Class: Place it in app/Filters/AuthFilter.php.
<?php
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
class AuthFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
// Check if user is logged in
if (! session()->has('isLoggedIn')) {
// Redirect to login page with error message
return redirect()->to('/login')->with('error', 'Please log in to access that page.');
}
// Optional: Role-based check using arguments (e.g., 'auth:admin')
if ($arguments !== null && is_array($arguments)) {
$userRole = session()->get('userRole') ?? 'guest';
if (! in_array($userRole, $arguments)) {
return redirect()->to('/access-denied')->with('error', 'Insufficient permissions.');
}
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// Optional: Modify response after controller (e.g., add header)
// $response->setHeader('X-Auth-Status', 'Authenticated');
return $response;
}
}
Explanation:
- In the before() method, we check if the user is logged in using a hypothetical auth service. If not, we return a redirect response, which stops further execution (no controller is called).
- Returning nothing (or explicitly returning null) allows the request to proceed.
- If you return the $request object, you can modify it (e.g., add headers or change URI) without halting.
- The after() method runs post-controller. Here, we add a custom header to the response. Note: You can't halt execution in after(); only modify the response.
- The $arguments parameter (available since 4.4.0) lets you pass custom data from configuration, like roles: ['admin', 'editor']. In the method, you could check if (in_array('admin', $arguments)).
This filter is now ready to be configured and applied.
5 Configuring Filters in app/Config/Filters.php
All filter setup happens in app/Config/Filters.php. This file defines aliases, globals, methods, and route-specific applications.
First, define an alias for your filter in the $aliases array:
public array $aliases = [
'auth' => \App\Filters\AuthFilter::class,
// Built-in ones are already there, e.g., 'csrf' => \CodeIgniter\Filters\CSRF::class,
];
You can alias multiple filters as a group:
public array $aliases = [
'adminProtect' => [
\App\Filters\AuthFilter::class,
\App\Filters\RoleCheck::class,
],
];
Required Filters (New in 4.5.0)
These run on every request, even if routes do not exist:
public array $required = [
'before' => ['forcehttps', 'pagecache'],
'after' => ['toolbar'],
];
Use sparingly for essentials like HTTPS enforcement.
Applying Filters Globally
Global filters apply to all requests unless excluded. Configure them in the $globals array:
public array $globals = [
'before' => [
'honeypot', // Spam protection
'csrf' => ['except' => 'api/*'], // Exclude APIs from CSRF
],
'after' => [
'toolbar' => ['except' => 'api/*'],
'secureheaders',
],
];
Explanation:
- The except key skips filters for matching URIs (wildcards like * are supported).
- For multiple exclusions: 'except' => ['api/*', 'public/*'].
- Globals are great for app-wide security but can impact performance if overused.
Now let us look at the complete Filters.php configuration for our authentication example:
Complete Code app/Config/Filters.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use App\Filters\AuthFilter;
class Filters extends BaseConfig
{
public array $aliases = [
'csrf' => \CodeIgniter\Filters\CSRF::class,
'toolbar' => \CodeIgniter\Filters\DebugToolbar::class,
'honeypot' => \CodeIgniter\Filters\Honeypot::class,
'auth' => AuthFilter::class, // Our custom filter
];
public array $globals = [
'before' => [
// 'honeypot',
// 'csrf',
],
'after' => [
'toolbar',
],
];
public array $methods = [];
public array $filters = [
'auth' => ['before' => ['admin/*']], // Apply to all admin routes
// Example with arguments: 'auth:admin,editor' => ['before' => ['admin/*']],
];
public array $required = [];
}
6 Create Controllers
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
class Auth extends BaseController
{
public function login()
{
if (session()->has('isLoggedIn')) {
return redirect()->to('/admin');
}
return view('login');
}
public function attemptLogin()
{
// Fake authentication (replace with real DB check)
$email = $this->request->getPost('email');
$password = $this->request->getPost('password');
if ($email === 'admin@example.com' && $password === 'secret') {
session()->set([
'isLoggedIn' => true,
'userRole' => 'admin', // For role-based example
'userEmail' => $email,
]);
return redirect()->to('/admin');
}
return redirect()->back()->with('error', 'Invalid credentials');
}
public function logout()
{
session()->destroy();
return redirect()->to('/login');
}
}
?>
Create app/Controllers/Admin.php(Protected Controller)
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
class Admin extends BaseController
{
public function index()
{
return redirect()->to('/admin/dashboard');
}
public function dashboard()
{
echo "<h1>Welcome to Admin Dashboard!</h1>";
echo "<p>You are logged in as: " . session()->get('userEmail') . "</p>";
echo "<a href='/logout'>Logout</a>";
}
}
?>
Create app/Controllers/Home.php
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
class Home extends BaseController
{
public function index()
{
return redirect()->to('/login');
}
public function accessDenied()
{
echo "<h1>Access Denied</h1><p>You don't have permission.</p><a href='/login'>Back to Login</a>";
}
}
?>
7 Login View
<!DOCTYPE html>
<html>
<head><title>Login</title></head>
<body>
<h2>Login</h2>
<?php if (session()->getFlashdata('error')): ?>
<p style="color:red;"><?= esc(session()->getFlashdata('error')) ?></p>
<?php endif; ?>
<form method="post" action="/login">
<?= csrf_field() ?>
<label>Email: <input type="email" name="email" required /></label><br><br>
<label>Password: <input type="password" name="password" required /></label><br><br>
<button type="submit">Login</button>
</form>
<p><strong>Demo Credentials:</strong> admin@example.com / secret</p>
</body>
</html>
8 Routes Configuration
In app/Config/Routes.php, define the routes for the authentication and admin controllers. These routes map URLs to the controller methods we created above:
$routes->get('/', 'Home::index');
$routes->get('/login', 'Auth::login');
$routes->post('/login', 'Auth::attemptLogin');
$routes->get('/logout', 'Auth::logout');
$routes->get('/admin', 'Admin::index');
$routes->get('/admin/dashboard', 'Admin::dashboard');
$routes->get('/access-denied', 'Home::accessDenied');
Notice that we do not need to specify the filter in the routes file because we already configured it in Filters.php to apply to all admin/* routes. However, if you prefer route-level filter assignment, you could also use the filter option like this:
$routes->get('/admin/dashboard', 'Admin::dashboard', ['filter' => 'auth']);
Both approaches work. The $filters array in Filters.php is better for managing many routes centrally, while inline filters are useful for one-off cases.
9 Folder Structure
10 Run and Test
Start the built-in development server using the following Spark command:
php spark serve
Now test the filter behavior step by step:
- Visit http://localhost:8080/ — this redirects you to /login since the Home controller redirects there.
- Try accessing http://localhost:8080/admin/dashboard directly without logging in — the AuthFilter will intercept the request and redirect you back to /login with the error message.
- Log in using the demo credentials: admin@example.com / secret.
- After successful login, you are redirected to /admin/dashboard — the filter allows access because the session now contains isLoggedIn.
- Click the Logout link — the session is destroyed and you are redirected back to /login.
Troubleshooting Tips:
- If the filter does not seem to work, run php spark filter:check get /admin/dashboard to verify which filters are applied to that route.
- Make sure the $filters array in Filters.php uses the correct URI pattern. The pattern admin/* matches /admin/dashboard but not /admin alone. To cover both, add both patterns or use a global filter.
- If you get session errors, ensure the writable directory has proper write permissions.
To learn more about securing your CodeIgniter 4 application, check out our article on how to secure CodeIgniter 4 from SQL injection.
Related CodeIgniter 4 Tutorials
If you found this filters tutorial helpful, here are more CodeIgniter 4 guides from our site to help you build complete, production-ready applications:
- CodeIgniter 4 Authentication Tutorial: Build Login and Registration System from Scratch — Take the authentication filter from this tutorial further by building a full login and registration system with database integration and CI4 Shield.
- Role-Based Login System in CodeIgniter 4 — Learn how to implement admin and user roles, which pairs perfectly with the role-based filter arguments covered in this guide.
- CodeIgniter 4 REST API CRUD Example with Postman — Build a RESTful API and use filters to handle API token authentication and CORS headers.
- How to Secure Your CodeIgniter 4 Application from SQL Injection — Combine filter-based security with proper query handling to build truly secure applications.
- Boost CodeIgniter 4 Performance and Page Speed — Learn how to use the page cache filter and other techniques to optimize your CI4 application speed.
- How to Build a Simple Blog in CodeIgniter 4 with MySQL and Bootstrap — A beginner-friendly project where you can practice applying filters to protect admin blog management routes.
12 Conclusion
Filters in CodeIgniter 4 provide a clean, reusable way to handle cross-cutting concerns in your application. By centralizing logic like authentication into filters, you keep controllers slim, maintainable, and free from repeated boilerplate code.
In this tutorial, we covered the key concepts of CI4 filters: how before and after filters work, how to create a custom authentication filter with role-based access control, how to configure filters globally and for specific routes, and how to test everything. These patterns form the foundation of secure CI4 applications.
Next Steps:
- Replace the hardcoded credentials with a proper database-driven authentication system. See our CodeIgniter 4 authentication tutorial for a complete implementation.
- Add more filters for rate limiting, API token validation, or maintenance mode.
- Explore the official CI4 filters documentation for the latest features and updates.
If you are deciding between CodeIgniter and Laravel for your next project, our CodeIgniter 4 vs Laravel 12 comparison can help you make the right choice.
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
Before filters run prior to the controller and can halt execution by returning a response. After filters run post-controller and can only modify the response.
Use the 'except' key in the globals configuration, for example: 'csrf' => ['except' => 'api/*'].
Yes, starting from version 4.4.0, you can pass arguments like 'auth:admin' which are available in the filter's methods.
Built-in filters include csrf, toolbar, honeypot, invalidchars, secureheaders, forcehttps, pagecache, and performance.
Use the spark command: spark filter:check
