Перейти к содержанию

PHP-FPM

Production configuration for NGINX with PHP-FPM (FastCGI Process Manager).

Security First

Many online guides contain security issues or don't handle PATH_INFO correctly. This guide addresses both concerns.


Quick Start

Basic Configuration

server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        # Prevent execution of non-existent PHP files
        try_files $uri =404;

        # FastCGI settings
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;

        # Required parameters
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;

        # Security: block httpoxy attacks
        fastcgi_param HTTP_PROXY "";
    }

    # Block access to hidden files
    location ~ /\. {
        deny all;
    }
}

Connection Methods

Lower latency for local connections:

fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;

PHP-FPM pool config (/etc/php/8.2/fpm/pool.d/www.conf):

listen = /var/run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

TCP Socket

For remote PHP-FPM servers or containers:

fastcgi_pass 127.0.0.1:9000;

PHP-FPM pool config:

listen = 127.0.0.1:9000

Upstream Pool

For load balancing across multiple PHP-FPM servers:

upstream php {
    least_conn;
    server unix:/var/run/php/php8.2-fpm.sock weight=5;
    server 10.0.0.2:9000 backup;
    keepalive 16;
}

server {
    location ~ \.php$ {
        fastcgi_pass php;
        fastcgi_keep_conn on;
        # ... other params
    }
}

FastCGI Parameters

Create /etc/nginx/fastcgi_params or /etc/nginx/snippets/fastcgi-php.conf:

# Core parameters
fastcgi_param QUERY_STRING       $query_string;
fastcgi_param REQUEST_METHOD     $request_method;
fastcgi_param CONTENT_TYPE       $content_type;
fastcgi_param CONTENT_LENGTH     $content_length;

# Script paths
fastcgi_param SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param PATH_INFO          $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED    $document_root$fastcgi_path_info;
fastcgi_param REQUEST_URI        $request_uri;
fastcgi_param DOCUMENT_URI       $document_uri;
fastcgi_param DOCUMENT_ROOT      $document_root;
fastcgi_param SERVER_PROTOCOL    $server_protocol;

# Server info
fastcgi_param GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param SERVER_SOFTWARE    nginx/$nginx_version;
fastcgi_param REMOTE_ADDR        $remote_addr;
fastcgi_param REMOTE_PORT        $remote_port;
fastcgi_param SERVER_ADDR        $server_addr;
fastcgi_param SERVER_PORT        $server_port;
fastcgi_param SERVER_NAME        $server_name;

# HTTPS detection
fastcgi_param HTTPS              $https if_not_empty;
fastcgi_param REDIRECT_STATUS    200;

# Security
fastcgi_param HTTP_PROXY         "";

PATH_INFO Handling

For frameworks using PATH_INFO (e.g., /index.php/controller/action):

location ~ [^/]\.php(/|$) {
    # Split PATH_INFO from script name
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;

    # Verify script exists
    try_files $fastcgi_script_name =404;

    # Set PATH_INFO
    set $path_info $fastcgi_path_info;
    fastcgi_param PATH_INFO $path_info;

    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Testing PATH_INFO

Create test.php:

<?php
echo "SCRIPT_NAME: " . $_SERVER['SCRIPT_NAME'] . "\n";
echo "PATH_INFO: " . ($_SERVER['PATH_INFO'] ?? 'not set') . "\n";
echo "REQUEST_URI: " . $_SERVER['REQUEST_URI'] . "\n";

Test with: curl http://localhost/test.php/foo/bar?v=1

Expected output:

SCRIPT_NAME: /test.php
PATH_INFO: /foo/bar
REQUEST_URI: /test.php/foo/bar?v=1


Performance Tuning

Buffer Settings

location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    # Buffers
    fastcgi_buffer_size 32k;
    fastcgi_buffers 16 16k;
    fastcgi_busy_buffers_size 32k;

    # Timeouts
    fastcgi_connect_timeout 60;
    fastcgi_send_timeout 180;
    fastcgi_read_timeout 180;

    # Keep connections alive
    fastcgi_keep_conn on;
}

PHP-FPM Pool Tuning

/etc/php/8.2/fpm/pool.d/www.conf:

; Process manager
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500

; Timeouts
request_terminate_timeout = 180

; Status page (optional)
pm.status_path = /fpm-status
ping.path = /fpm-ping

Enable Status Page

location ~ ^/(fpm-status|fpm-ping)$ {
    access_log off;
    allow 127.0.0.1;
    allow ::1;
    deny all;
    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Security

Block PHP in Uploads

# Block PHP execution in upload directories
location ~* /(?:uploads|files|media)/.*\.php$ {
    deny all;
}

Limit PHP Locations

Only allow PHP in specific directories:

# Block PHP everywhere except allowed paths
location ~ \.php$ {
    return 403;
}

# Allow PHP in root and specific paths only
location ~ ^/(?:index|api|admin)\.php$ {
    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Rate Limiting

limit_req_zone $binary_remote_addr zone=php:10m rate=10r/s;

location ~ \.php$ {
    limit_req zone=php burst=20 nodelay;
    # ... fastcgi config
}

FastCGI Caching

# Define cache zone (http block)
fastcgi_cache_path /var/cache/nginx/fastcgi
    levels=1:2
    keys_zone=php_cache:100m
    inactive=60m
    max_size=1g;

server {
    set $no_cache 0;

    # Don't cache POST requests
    if ($request_method = POST) { set $no_cache 1; }

    # Don't cache authenticated users
    if ($http_cookie ~* "session_id") { set $no_cache 1; }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        # Caching
        fastcgi_cache php_cache;
        fastcgi_cache_valid 200 10m;
        fastcgi_cache_bypass $no_cache;
        fastcgi_no_cache $no_cache;
        add_header X-Cache-Status $upstream_cache_status;
    }
}

Common Issues

Issue Solution
502 Bad Gateway PHP-FPM not running, wrong socket path, or permission denied
504 Gateway Timeout Increase fastcgi_read_timeout and PHP's max_execution_time
File not found Check SCRIPT_FILENAME parameter and root directive
Blank page Check PHP error logs, ensure display_errors = On in development
Permission denied Socket permissions - ensure NGINX user can access the socket

Debug 502 Errors

# Check PHP-FPM is running
systemctl status php8.2-fpm

# Check socket exists
ls -la /var/run/php/php8.2-fpm.sock

# Check NGINX error log
tail -f /var/log/nginx/error.log

# Check PHP-FPM log
tail -f /var/log/php8.2-fpm.log

Socket Permissions

Ensure NGINX and PHP-FPM use the same user/group:

; PHP-FPM pool config
user = www-data
group = www-data
listen.owner = www-data
listen.group = www-data

See Also