Diagnosing 404 Errors in CodeIgniter 4: Routing & Rewrite Checklist
A concise 404‑diagnosis guide for CodeIgniter 4. Check .htaccess, base_url, routes, and server rewrites to fix clean‑URL failures quickly.
10 Oct 2025, 10:38 UTC

Recognizable Condition
You request a route that should map to an existing controller, but CodeIgniter 4 returns its 404 page. The browser URL stays clean (for example, /home/index) and does not visibly redirect to index.php. The same URI may work if you manually append index.php, which points to a rewrite or routing problem rather than a missing controller.
Short Cause / Diagnostic Table
| Possible Cause | What to Look For |
|---|---|
| .htaccess missing or invalid | No rewrite block in public/, or rules force an external redirect |
| Server rewrite disabled | Apache mod_rewrite not loaded; Nginx lacks try_files |
| Route not defined | URI absent from app/Config/Routes.php or wrong namespace |
| base_url misconfigured | Generated links point to wrong host or path (empty value auto-detects in CI4) |
Ordered Checks
- Confirm PHP version and framework bootstrap
CodeIgniter 4 requires PHP 8.1 or newer. Run in your project root or terminal:
php -vExpected: PHP 8.1+. To confirm the framework is bootstrapped, check the version constant defined in
system/CodeIgniter.php:grep CI_VERSION system/CodeIgniter.phpRisk: Running PHP commands on a production server requires appropriate permissions; use a deployment user rather than root where possible.
- Inspect the public/.htaccess file
CodeIgniter 4 expects the web root to be the
publicdirectory. Check that the file exists and contains an internal rewrite rule:ls -l public/.htaccessExpected content (standard CI4 block):
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php/$1 [L]Do not use
R=301here; that forces a visible browser redirect and breaks clean URLs. The rule must be an internal rewrite. - Check base_url in app/Config/App.php
Open
app/Config/App.phpand locatepublic $baseURL. In CI4, leaving it empty triggers auto-detection, which usually works. If it is set, it must include the scheme, host, and trailing slash:public $baseURL = 'https://example.com/';A wrong value here breaks helper-generated links but does not usually cause a 404 on a directly typed URL.
- Review app/Config/Routes.php
Ensure the requested URI is mapped. For example, to route
/home/indexto the defaultHomecontroller:$routes->get('home/index', 'Home::index');Also verify the controller namespace matches its directory. The default CI4 namespace is
App\Controllers, and the file should live inapp/Controllers/. - Validate server rewrite configuration
On Apache, confirm the rewrite module is loaded. Depending on your distribution, run one of:
apachectl -M | grep rewrite # or apache2ctl -M | grep rewriteExpected:
rewrite_moduleappears in the list.On Nginx, inspect the server block that handles your site.
nginx -Tdumps the entire configuration, so pipe it to a pager or grep for your server name:nginx -T 2>&1 | grep -A 10 'server_name example.com'Look for a
try_filesdirective that falls back toindex.php:try_files $uri $uri/ /index.php?$query_string; - Test the route directly
Request
https://example.com/home/indexin a browser or with curl:curl -I https://example.com/home/indexExpected: HTTP 200 (or your application's normal response). If you receive a 404, the request never reached
index.php. If you receive a redirect toindex.php, your rewrite rule is misconfigured.
Fixes Tied to Findings
- Missing or invalid .htaccess – Place the standard block above in
public/.htaccess. Ensure the file is readable by the web server user. Restart Apache or reload Nginx after changes. - mod_rewrite disabled – Enable it on Debian/Ubuntu with
sudo a2enmod rewrite, then restart Apache. On Nginx, add or correct thetry_filesdirective in thelocation /block and runsudo nginx -tbefore reloading. - Route missing or namespace mismatch – Add the route to
app/Config/Routes.phpor correct thenamespacedeclaration and file path inapp/Controllers/. - base_url wrong – Update
public $baseURLto the correct absolute URL with trailing slash. Clear any cached config if you use config caching.
Verification After Fix
- Request the route again with
curl -I. The status should be 200 and the browser URL should remain clean. - Check the server access log for the request path; it should show the original URI, not
index.php. - If you changed Nginx or Apache configuration, confirm syntax with
sudo nginx -torsudo apachectl configtestbefore reloading.
Limitations
This guide covers Apache and Nginx on standard Linux deployments. It does not address IIS, shared hosting control panels, subdirectory installations, or reverse-proxy setups where the rewrite happens upstream. It also assumes a default CI4 project structure; heavily customized bootstrap files or filters may require additional debugging.
Escalation Criteria
Escalate to a senior developer or server administrator if:
- The rewrite module is enabled and
.htaccessis correct, but clean URLs still 404. - The document root cannot be changed to
public/(common on shared hosts). - Server logs show permission denials on
public/.htaccessorpublic/index.php. - A parent directory
.htaccessor Nginxincludeoverrides the intended rules.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.