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.



CodeIgniter 4 Filters: A Complete Guide with Examples

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

First, make sure your computer has a composer.
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

To create a filter, you need to make a class that implements the CodeIgniter\Filters\FilterInterface. This interface requires two methods: before() and after().

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

Create app/Controllers/Auth.php(Login Controller)

<?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

File: app/Views/login.php
    
<!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.

If you found this filters tutorial helpful, here are more CodeIgniter 4 guides from our site to help you build complete, production-ready applications:

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:

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.

Revathi M - PHP and CodeIgniter Developer

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 , e.g., spark filter:check get /admin.