Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
CodeIgniterDevOpsPHPResource guide

CodeIgniter 4 production deployment and security checklist

A practical pre-launch checklist for CodeIgniter 4.7 apps: production environment, document root, Composer and optimisation, HTTPS and secure headers, CSRF and sessions, file uploads, proxies and logging, with commands to verify each item.

Cloudsoft Zone

Updated 8 min read

Rows of open black server racks in an empty data centre room with a raised tiled floor
Photograph: Carl Lender from Sunrise, USA / CC BY 2.0, via Wikimedia Commons
In this article

A CodeIgniter 4 application that works on a laptop is not ready for the internet. Development mode shows full stack traces, Composer development packages are still installed, the document root may expose files it should not, and security filters that ship with the framework are often left switched off. Most real-world incidents with small PHP applications come from configuration like this rather than exotic attacks.

This checklist collects the production settings that the CodeIgniter 4.7 user guide recommends, from the deployment page, the security guidelines (which map framework features to the OWASP Top 10 and OWASP API Security Top 10), the filters reference and the latest change logs. Use it before your first launch and again before each major release. Each item says what to do, why, and how to check it.

The checklist assumes CodeIgniter 4.7.4, the current release, on PHP 8.2 or newer. Where a setting differs in Worker Mode, that is called out.

1. Runtime and framework version

  • Run a supported PHP version. CodeIgniter 4.7 needs PHP 8.2+, and the user guide warns that PHP 8.2 reaches end of life on 31 December 2026. A new server should start on PHP 8.3 or later.
  • Be on the latest CodeIgniter patch release. The project maintains only the latest version. Version 4.7.4 (7 July 2026) fixed an SQL injection in deleteBatch(), a path traversal in UploadedFile::move(), an upload validation bypass in is_image and mime_in, and HTTPS detection that trusted client-supplied headers.
  • Check PHP settings with Spark. php spark phpini:check (added in 4.5.0) lists important php.ini values next to the recommended production values.
Shell
php -v
composer show codeigniter4/framework | grep versions
php spark phpini:check
Server room with rack cabinets holding servers, network equipment and a small monitor
Point the web server at public/ so application code, .env and writable/ stay out of reach.

2. Environment and error display

CodeIgniter uses the CI_ENVIRONMENT value to decide how much to reveal. In production, error output is disabled; in development, all PHP errors are shown in the browser. If the variable is not set, the framework defaults to production, which is a safe default, but a .env file copied from a developer machine can silently switch it.

Shell
php spark env            # prints the current environment
php spark env production # writes CI_ENVIRONMENT = production into .env

On nginx you can set it per site with fastcgi_param CI_ENVIRONMENT "production";, and on Apache with SetEnv CI_ENVIRONMENT production. If you need a staging environment, copy app/Config/Boot/production.php to staging.php; do not use testing, which is reserved for PHPUnit.

Keep secrets (database passwords, encryption keys, API keys) in .env or real environment variables, never in committed config classes. Since 4.7 you can also move the .env file outside the project with Paths::$envDirectory.

3. Document root and file permissions

index.php lives in public/, not the project root. Point the web server's document root at public/ so app/, vendor/, writable/ and .env are not reachable over HTTP.

Nginx
server {
    server_name example.com;
    root /var/www/example.com/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param CI_ENVIRONMENT "production";
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }

    location ~ /\. {
        deny all;
    }
}

Adjust the socket path to your PHP-FPM installation. On shared hosting where you cannot change the document root, the deployment guide describes two fallbacks: upload the contents of public/ to public_html and the rest of the project outside it (preferred), or, as a last resort, a root .htaccess that rewrites to public/ and denies dot files.

Only writable/ needs to be writable by the web server user. Do not make the whole project writable, and never use world-writable permissions in production. Check that .git, backups and editor swap files are not inside public/; the OWASP guidance quoted in the CodeIgniter security guidelines calls this out explicitly.

4. Dependencies and optimisation

  • Install without dev packages: composer install --no-dev removes test tools and debug helpers from the server and shrinks vendor/.
  • Optimise (traditional PHP-FPM only): php spark optimize removes dev packages and enables config caching and file-locator caching. Cached config does not change when you edit .env or a config file, so clear the cache as part of each deploy.
  • Worker Mode exception: do not run spark optimize or enable those caches when running under FrankenPHP worker mode; the user guide warns they cause stale data there. Restart workers after each deploy instead.
  • Enable OPcache and, if one application owns the PHP handler, consider preloading with the preload script the framework provides.
  • Audit dependencies: run composer audit in CI and before releases to catch known vulnerabilities.

5. HTTPS, headers and proxies

Turn on Config\App::$forceGlobalSecureRequests = true. With the forcehttps filter (in $required['before'] by default), insecure requests are redirected to HTTPS and an HSTS header is sent. Then enable the security filters in app/Config/Filters.php:

PHP
public array $globals = [
    'before' => [
        'csrf' => ['except' => ['api/*']],
        'invalidchars',
    ],
    'after' => [
        'secureheaders',
    ],
];

invalidchars rejects input containing invalid UTF-8 or control characters. secureheaders adds common security response headers; extend CodeIgniter\Filters\SecureHeaders if you need different values. Configure a Content Security Policy in app/Config/ContentSecurityPolicy.php; 4.7 added CSP Level 3 directives and hash sources.

If the application sits behind a load balancer or CDN, list the proxy addresses in Config\App::$proxyIPs. Since 4.7.4, isSecure() trusts X-Forwarded-Proto only from those addresses, and the same setting controls which forwarded client IPs getIPAddress() believes, which matters for rate limiting and logs.

6. Forms, sessions and authentication

  • CSRF: keep the csrf filter on for browser forms. The session-based mode ($csrfProtection = 'session') is what Shield recommends when you use its session authenticator, and enabling token randomisation ($tokenRandomize, off by default) helps against compression side-channel attacks such as BREACH.
  • Escape output: use esc() in views for any data that came from users or third parties.
  • Validate all input with the Validation library or model rules, and use model $allowedFields to block mass assignment.
  • Use Query Builder or bound queries, never string-concatenated SQL.
  • Authentication: use CodeIgniter Shield rather than a home-grown login, and apply its auth-rates filter to login, registration and token routes.
  • Rate limiting: the Throttler service implements a token bucket; it needs a real cache handler (not dummy), and Redis or Memcached performs best.
  • Session storage: on more than one server, use a shared handler such as Redis or the database rather than files.
Glowing padlock graphic over blue lines of program code on a dark background
Turn on the csrf, invalidchars and secureheaders filters and force HTTPS before launch.

7. File uploads

Uploads are the most common way a PHP application ends up running code it did not write. Validate with uploaded, max_size, ext_in and mime_in or is_image, store files in writable/uploads or object storage rather than public/, and always generate the stored filename yourself:

PHP
$file = $this->request->getFile('avatar');

if ($file !== null && $file->isValid() && ! $file->hasMoved()) {
    $file->move(WRITEPATH . 'uploads/avatars', $file->getRandomName());
}

4.7.4 made move() sanitise the client filename when you omit the second argument, but the user guide notes that a name you pass explicitly is your responsibility, so getRandomName() remains the safer choice.

8. Logging, monitoring and routes

  • Log failed logins and access-control failures; Shield records failed token logins by default.
  • Ship writable/logs to a central log service or rotate it so it does not fill the disk.
  • Run php spark routes and php spark filter:check get /some/path to confirm that every sensitive route has the filters you expect.
  • Prefer defined routes. If you use auto-routing, use Auto Routing (Improved); the legacy mode lets any HTTP method reach a controller and can bypass method-based filters.
  • Remove debug routes, or restrict them with #[Restrict(environment: ['development'])] on 4.7.

9. Database changes during deploys

Run php spark migrate as a deploy step rather than by hand, and check php spark migrate:status afterwards. If more than one server or container may start the deploy at the same time, enable the distributed lock added in 4.7 by setting Config\Migrations::$lock = true, so only one process applies migrations. Write a working down() method for every migration, take a database backup before releases that change tables, and test the restore. A migration that cannot be rolled back is a decision you should make on purpose, not discover during an incident.

Pre-launch checklist

CheckHow to verify
PHP 8.2+ (8.3+ preferred) and CodeIgniter 4.7.4 or newerphp -v, composer show codeigniter4/framework
Environment is productionphp spark env
php.ini values reviewedphp spark phpini:check
Document root is public/Request /.env and /app/Config/App.php; both must fail
No dev packagescomposer install --no-dev in the deploy script
HTTPS forced, HSTS sentcurl -I http://example.com shows a redirect
Security filters onphp spark filter:check on key routes
Proxy IPs configuredReview Config\App::$proxyIPs
Uploads stored outside public/ with random namesCode review
Dependencies auditedcomposer audit

Next steps

Put the verifiable items into your deployment script so they run every time, not only at launch: set the environment, install without dev packages, clear and rebuild caches, run migrations, and fail the deploy if composer audit reports a known vulnerability. Subscribe to the CodeIgniter4 security advisories on GitHub so the next patch release reaches you the day it is published.

Frequently asked questions

  • What should CI_ENVIRONMENT be on a live server?

    production. It disables error output. CodeIgniter defaults to production when the value is not set, but check that a copied .env file has not switched it to development.

  • Should I run php spark optimize on every deploy?

    On a traditional PHP-FPM server it is useful, but cached config ignores later changes to .env, so clear and rebuild it on each deploy. Do not use it with Worker Mode.

  • My host will not let me change the document root. What now?

    Upload the contents of public/ to the host's document root and the rest of the project to a folder outside it. A root .htaccess rewrite is only a last resort.

  • Do I need the CSRF filter for an API?

    CSRF protects cookie-based sessions. Endpoints authenticated only with bearer tokens can be excluded, but keep CSRF on for browser forms.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Deployment — CodeIgniter Foundation
  2. Security Guidelines — CodeIgniter Foundation
  3. Handling Multiple Environments — CodeIgniter Foundation
  4. Controller Filters — CodeIgniter Foundation
  5. Running Your App — CodeIgniter Foundation
  6. Throttler — CodeIgniter Foundation
  7. Version 4.7.4 change log — CodeIgniter Foundation, 2026-07-07
  8. Command-line interface: audit — Composer

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.

  • Plush elePHPant toy from Heroku, a version of the PHP community mascot, photographed close up

    Laravel

    Upgrading from Laravel 12 to Laravel 13: a practical guide

    Laravel 12 lost bug-fix support in August 2026. This guide covers the PHP 8.3 requirement, Composer changes, the CSRF middleware rename, cache and session hardening, low-impact changes that often bite, and a safe rollout plan.

    By Cloudsoft Zone