Nginx + PHP-FPM cheatsheet.

Basic config

server {
    listen 80;
    server_name example.com;
    root /var/www/html/public;
    index index.php;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        # Or: fastcgi_pass 127.0.0.1:9000;
    }
    
    location ~ /\.ht { deny all; }
}

fastcgi-php.conf snippet

fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
try_files $fastcgi_script_name =404;

Laravel

server {
    listen 80;
    server_name app.example.com;
    root /var/www/app/public;
    index index.php;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        internal;
    }
    
    location ~ \.php$ { return 404; }
}

Only index.php is served as PHP; prevents direct script execution.

WordPress

server {
    root /var/www/wordpress;
    index index.php;
    
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
    
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
    
    # WP-specific
    location = /favicon.ico { log_not_found off; access_log off; }
    location = /robots.txt { log_not_found off; access_log off; allow all; }
    
    location ~* \.(css|js|jpg|png|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    
    # Block xmlrpc
    location = /xmlrpc.php { deny all; }
    
    # Deny PHP in uploads
    location ~* /wp-content/uploads/.*\.php$ {
        deny all;
    }
}

PHP-FPM pool

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

[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data

pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500

request_terminate_timeout = 60s
catch_workers_output = yes
clear_env = no

opcache (php.ini)

opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0    ; prod: never check; reload to update
opcache.jit_buffer_size=64M
opcache.jit=tracing

In dev, set validate_timestamps=1.

fastcgi_cache

fastcgi_cache_path /var/cache/nginx/fcgi
    levels=1:2
    keys_zone=fcgi:50m
    max_size=1g
    inactive=60m;

server {
    set $skip_cache 0;
    if ($request_method = POST) { set $skip_cache 1; }
    if ($query_string != "") { set $skip_cache 1; }
    if ($request_uri ~* "/wp-admin/|/wp-login.php") { set $skip_cache 1; }
    if ($http_cookie ~* "wordpress_logged_in") { set $skip_cache 1; }
    
    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_cache fcgi;
        fastcgi_cache_key "$scheme$host$request_uri";
        fastcgi_cache_valid 200 60m;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        add_header X-Cache $upstream_cache_status;
        include snippets/fastcgi-php.conf;
    }
}

Massive speedup for WP.

Status page

location ~ ^/(fpm-status|fpm-ping)$ {
    access_log off;
    allow 127.0.0.1;
    deny all;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    include fastcgi_params;
}

pm.status_path = /fpm-status in pool config.

Upload size

client_max_body_size 100m;
; php.ini
upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 256M
max_execution_time = 60

Must match in both nginx and php.

Timeouts

fastcgi_connect_timeout 10s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;

Multiple PHP versions

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

# Legacy app
location /legacy/ {
    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
    }
}

Hide sensitive files

location ~* /(?:config|vendor|.env|composer\.(json|lock)|package(-lock)?\.json) {
    deny all;
}

Performance tips

  1. Unix socket > TCP (lower overhead).
  2. opcache.enable.
  3. fastcgi_cache for read-heavy.
  4. CDN for static assets.
  5. Right-size pm.max_children to RAM.

Common mistakes

  • pm.max_children too high → OOM.
  • TCP fastcgi_pass with localhost (resolves DNS) — use 127.0.0.1.
  • validate_timestamps=0 in dev → changes don’t take effect.
  • Allowing PHP execution in upload dirs → RCE risk.
  • Missing internal; on index.php → arbitrary php executable.

Read this next

If you want my Laravel + WP nginx templates, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .