Skip to content
Let’s plan the right software for your processes. Call us for a demo or a quote: +90 546 737 48 29

TR EN DE

Nginx and PHP-FPM: how PHP sites run on Nginx

On a site that runs on Apache, PHP mostly works "by itself", because Apache can run PHP inside itself as a module. With Nginx things are different: Nginx cannot run PHP code. Instead it passes the request to PHP-FPM (FastCGI Process Manager), which waits as a separate service to run PHP, takes the response and sends it to the visitor.

The language the two speak to each other is a protocol called FastCGI. So there are two separate programs, two separate configurations and a connection between them. Most of the problems people have with PHP sites on Nginx ("502 Bad Gateway", a blank page, a PHP file being downloaded) come from a setting at one end of this connection. This guide first explains how the arrangement works and then walks through the correct configuration step by step. If you are new to Nginx, have a look at the what is Nginx guide first.

In brief

  • Nginx does not run PHP; it passes .php requests to PHP-FPM over FastCGI.
  • The fastcgi_pass address must match the listen value in the PHP-FPM pool.
  • try_files prevents non-existent files from being sent to PHP.
  • If the socket is missing or its permissions are wrong, the visitor sees a 502.

What you need

  • Administrator (sudo) rights on the server
  • Nginx and the PHP-FPM package installed
  • The site's Nginx configuration file and a backup of it
  • Access to the Nginx and PHP-FPM logs

Note

The socket and file paths in this guide are examples. The PHP-FPM socket path, service name and configuration folder depend on the distribution and the PHP version; verify the values on your own server as shown in step 2.

On this page

Configuring Nginx and PHP-FPM step by step

  1. Understand the flow: who does what?

    When a request arrives, the work is divided as follows:

    • Static files (images, CSS, JavaScript): Nginx reads them from disk and sends them directly; PHP-FPM is not involved at all.
    • PHP requests: Nginx passes the request to PHP-FPM together with the information about which file is to be run. PHP-FPM runs the file with one of its waiting worker processes and hands the output back to Nginx.

    Nginx and PHP-FPM talk to each other in one of two ways:

    • Unix socket: A special file on disk (e.g. /run/php/php-fpm.sock). Only processes on the same machine can use it; access is controlled by file permissions.
    • TCP address: An address and port such as 127.0.0.1:9000. This is needed if PHP-FPM runs on another machine or in a container.

    If the two are on the same machine, a Unix socket is the common choice; whichever you pick, the same value must be written on both sides.

    Diagram: the browser request reaches Nginx; Nginx serves static files and passes PHP requests over FastCGI through a socket to the PHP-FPM pool : Enlarge
  2. Check that PHP-FPM is running and find where it listens

    First make sure the PHP-FPM service is running, then find the listen line in the pool file. This line shows the socket or address that Nginx will connect to.

    Bash
    # Is PHP-FPM running? (the service name depends on the distribution)
    systemctl status php-fpm           # RHEL, AlmaLinux and similar
    systemctl status 'php*-fpm'        # Debian, Ubuntu: the name contains the version
    
    # Which socket or address does the pool listen on?
    sudo grep -R "^listen" /etc/php-fpm.d/ /etc/php/ 2>/dev/null
    
    # Does the socket file exist, and what are its owner and permissions?
    ls -l /run/php/ /run/php-fpm/ 2>/dev/null

    The service name and the paths depend on the distribution: on Debian and Ubuntu the service name and the paths contain the PHP version (e.g. a socket with a version number under /run/php/, pool files under /etc/php/<version>/fpm/pool.d/); on RHEL, AlmaLinux and similar systems the service is mostly php-fpm, the pool folder is /etc/php-fpm.d/ and the socket is under /run/php-fpm/. Hosting control panels may use their own paths. Do not guess; use whatever the listen line says.

  3. Route PHP requests to PHP-FPM in the server block

    The server block below shows the basic layout for a site that runs on PHP.

    Nginx
    server {
        listen 80;
        server_name example.com www.example.com;
    
        root /var/www/example.com/public;
        index index.php index.html;
    
        location / {
            try_files $uri $uri/ =404;
        }
    
        location ~ \.php$ {
            # Do not send the request to PHP if the file is not on disk
            try_files $uri =404;
    
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    
            # The socket path depends on the distribution; it must match "listen" in the pool
            fastcgi_pass unix:/run/php/php-fpm.sock;
            # For a pool that listens over TCP: fastcgi_pass 127.0.0.1:9000;
        }
    }

    What it means, line by line:

    • index index.php index.html; Says which file to look for when a folder is requested (e.g. /). If index.php is not in the list, the home page does not open or a 403 is returned.
    • try_files inside location /: If the requested address is a real file or folder it is served; otherwise a 404 is returned.
    • location ~ \.php$: Catches requests whose address ends in .php.
    • fastcgi_pass: The PHP-FPM address the request is passed to. For a Unix socket it is written as a path with the unix: prefix, for TCP in the form 127.0.0.1:9000.

    In applications that handle every request in a single index.php file (most frameworks and off-the-shelf content management systems), the location / block is slightly different: if no file is found, the request is routed to index.php instead of returning a 404.

    Nginx
    # Inside the "location /" block: if there is no file or folder, hand the request to index.php
    try_files $uri $uri/ /index.php?$query_string;

    Without this block the home page opens but the inner pages return 404. In Apache the same job is done by the rewrite rules in .htaccess; for moving them, see the guide on migrating from Apache to Nginx.

    Flow chart: a request arrives, a location is chosen, try_files checks that the file exists, fastcgi_pass passes the request to PHP-FPM, the response returns : Enlarge
  4. Supply fastcgi_params and SCRIPT_FILENAME correctly

    Nginx does not merely tell PHP-FPM "there is a request"; it sends the details of the request as FastCGI parameters: the request method, the query string, the visitor's IP address, the server name and so on. On the PHP side these appear in the $_SERVER array.

    • include fastcgi_params; Pulls in the file that ships with Nginx and defines these standard parameters.
    • SCRIPT_FILENAME is the parameter that tells PHP-FPM, with its full path, which file to run. The value $document_root$fastcgi_script_name joins the folder you gave with root and the name of the requested file.

    The fastcgi_params file usually does not contain the SCRIPT_FILENAME line; that is why it is written separately in the example. The file called fastcgi.conf, which also ships with Nginx, does contain this line; if you use that one you do not need to write the line a second time. The Debian and Ubuntu packages also include a ready-made snippet file that bundles these settings. Whichever you use, check that the parameter is defined once and correctly.

    If SCRIPT_FILENAME is missing or wrong, PHP-FPM cannot find the file: the visitor sees a blank page or the response "File not found.". The most common cause is a root directive that points to the wrong folder, or a different root defined inside the location.

  5. Do not send non-existent files to PHP

    The first line of the PHP block in the example, try_files $uri =404;, is a defensive measure. If the requested .php file does not exist on disk, Nginx does not pass the request to PHP-FPM at all and returns a 404 straight away.

    Why does this matter? Without the check, a .php address that does not really exist reaches PHP and, depending on PHP's path resolution setting, a different file may be run as PHP. If your site has an area where users can upload files, this can go as far as an uploaded file being executed as code. A one-line check closes this door.

    • This check works when Nginx and PHP-FPM see the same file system. If PHP-FPM is on a separate machine or in a container, Nginx cannot see the file and every request becomes a 404; in that arrangement the check has to be done on the PHP side.
    • As an additional layer, the security.limit_extensions setting in the PHP-FPM pool limits which extensions may be run as PHP; do not loosen this limit.
  6. Get to know the pool settings

    PHP-FPM manages its worker processes in groups called pools. Each pool has its own listen address, its own user and its own process limits. If there are several sites on the same server, defining a separate pool and a separate user for each makes it harder for a problem in one site to reach the files of another.

    PHP-FPM
    ; Pool file (e.g. www.conf); its path depends on the distribution
    [example]
    user = example
    group = example
    
    ; Must match fastcgi_pass in Nginx
    listen = /run/php/php-fpm.sock
    
    ; Owner and permissions of the socket: the Nginx user must have access
    ; (Debian, Ubuntu: www-data; RHEL, AlmaLinux: nginx)
    listen.owner = www-data
    listen.group = www-data
    listen.mode = 0660
    
    ; Process management (the numbers are a syntax example, not a recommendation)
    pm = dynamic
    pm.max_children = 10
    pm.start_servers = 2
    pm.min_spare_servers = 1
    pm.max_spare_servers = 3
    SettingWhat does it determine?
    listenThe socket or address PHP-FPM listens on. It must match fastcgi_pass in Nginx.
    user, groupWhich user the PHP code runs as. File read and write permissions apply according to this user.
    pmThe process management mode: static (a fixed number of processes), dynamic (between a lower and an upper limit depending on load) or ondemand (started as requests arrive, stopped when idle).
    pm.max_childrenThe upper limit on the number of worker processes that can run at the same time; in other words, the number of PHP requests that can be processed simultaneously.

    There is no one-size-fits-all number for pm.max_children. Every worker process uses memory; if the limit is too high the server runs out of memory at busy times, and if it is too low requests wait in a queue and a warning that the limit has been reached appears in the PHP-FPM log. The right value is found by measuring, looking at the server's free memory and at the memory your application uses per process. Read the numbers in the example as a syntax example, not as a recommendation.

    Diagram: the basic settings of a PHP-FPM pool; listen, user and group, the pm modes and pm.max_children : Enlarge
  7. Check socket permissions and the 502 error

    "502 Bad Gateway" means that Nginx could not get a valid response from the service behind it. On PHP sites the most common cause is that Nginx cannot connect to PHP-FPM. Do not guess the reason; the Nginx error log states it plainly.

    Phrase in the logLikely causeWhere to look
    No such file or directoryThe socket file does not exist: PHP-FPM is not running or the fastcgi_pass path is wrong (e.g. PHP was upgraded and the socket name changed)Service status; do listen and fastcgi_pass match?
    Permission deniedThe socket exists, but the user Nginx runs as has no permission to access itlisten.owner, listen.group, listen.mode
    Connection refusedNo service is listening on the TCP addressService status; address and port

    Because a Unix socket is a file, it has permissions. In the pool, listen.owner and listen.group set the owner of the socket and listen.mode sets its permissions. The user the Nginx worker processes run as (depending on the distribution, mostly www-data or nginx) must be able to access this socket with read and write permission. Opening the socket to everyone (0666) "solves" the problem but allows every user on the server to send requests to PHP-FPM; set the owner and group correctly instead.

    If the page gives an error after waiting for a while (504), or if the 502 only appears at busy times, the cause is not the connection but slowness on the PHP side or the process limit. For detailed diagnosis, see the guide on 502 and 504 errors.

    Diagram: three phrases in the Nginx error log for a 502 error and what they mean; socket missing, no permission, connection refused : Enlarge
  8. Turn off PHP execution in the upload folder

    There is no reason for PHP to run in the folder where visitors or administrators upload files (e.g. /upload/). Even if there is a flaw in your upload checks, a malicious uploaded file cannot be executed if files in that folder are not sent to PHP-FPM. This is a second line of defence that is not sufficient on its own but is effective.

    Nginx
    # Must be written BEFORE the general "location ~ \.php$" block
    location ~* ^/upload/.*\.(php|phtml|phar)$ {
        return 403;
    }

    Pay attention to two points: change the folder name to suit your own site, and write this block before the general location ~ \.php$ block. Nginx tries location blocks with regular expressions in the order they appear in the file and uses the first match; if the order is reversed the rule does not take effect. For upload security as a whole, see the guide on file upload security.

    Comparison: with PHP enabled in the upload folder an uploaded file can run; with it disabled the request is not sent to PHP-FPM and a 403 is returned : Enlarge
  9. Test, reload, verify

    After your changes, check the configuration first and then reload the relevant service. If you changed the Nginx file you need to reload Nginx; if you changed the pool file you need to reload PHP-FPM.

    Bash
    # Check the Nginx configuration and reload
    sudo nginx -t
    sudo systemctl reload nginx
    
    # If the pool file changed, reload PHP-FPM (the service name depends on the distribution)
    sudo systemctl reload php-fpm
    
    # If something is wrong, look at the latest error entries
    sudo tail -n 50 /var/log/nginx/error.log

    Then test from the browser: does the home page open, does an inner page open, does a non-existent .php address return 404, does a .php address in the upload folder return 403? If a PHP file is downloaded instead of being run, or its source code appears on screen, the request is not reaching the PHP block at all: check that the location ~ \.php$ block is inside the right server and that a reload has been done.

Common symptoms and their causes

SymptomLikely cause
502 Bad GatewayPHP-FPM is not running, the socket path is wrong or the socket permissions do not give Nginx access
Blank page or "File not found."SCRIPT_FILENAME is missing or wrong; root points to the wrong folder
The PHP file is downloadedThere is no location block for PHP, or the request falls into another block
Home page opens, inner pages return 404The try_files fallback to the front controller (index.php) is missing
403 on the home pageindex.php is not in the index directive, or the folder permissions do not allow Nginx to read it
413 when uploading a fileNginx's request body limit (client_max_body_size, default 1m) is exceeded; PHP's own upload limits also apply separately

In every case the first place to look is the logs: the Nginx error log (mostly /var/log/nginx/error.log) and the PHP-FPM log. Their locations may vary with the configuration.

A short security checklist

  • The PHP block contains try_files $uri =404;; non-existent files do not go to PHP.
  • PHP execution is turned off in upload folders.
  • Socket permissions are tight: only the Nginx user or group has access.
  • If PHP-FPM listens over TCP, it listens only on 127.0.0.1 or an internal network address; it is not open to the internet.
  • Each site runs in its own pool and as its own user; PHP processes do not run as the administrator (root).
  • The PHP user can write only to the folders it needs.
  • Error details are written to the log, not shown to the visitor.

For the server as a whole, see the guides on Nginx security settings and server and hosting security.

Frequently asked questions

On Nginx my PHP file is downloaded instead of being run. Why?

It means the request is not being passed to PHP-FPM. The site's server block must contain a location block that catches the .php extension, with fastcgi_pass inside it. If the block is there, check that it is inside the right server, that the configuration has been tested and reloaded, and that the browser is not showing an old response from its cache.

Should I use a Unix socket or 127.0.0.1:9000?

If Nginx and PHP-FPM are on the same machine, a Unix socket is the common choice; it does not open a network port and access is controlled by file permissions. If PHP-FPM runs on a separate machine or in a container, TCP is required. Whichever you choose, fastcgi_pass and listen in the pool must match.

I upgraded PHP and the site started returning 502. Why?

On some distributions the names of the socket file and the service contain the PHP version. When the version changes the socket path changes too, while Nginx keeps trying to connect to the old path. Find the listen value in the new pool file, update the fastcgi_pass line accordingly, then test and reload.

What should pm.max_children be?

There is no value that suits everyone. It depends on the memory available on the server and on the memory your application uses per process. Too high a value exhausts memory, too low a value keeps requests waiting. Set it by measuring on your own server and by watching the warnings in the PHP-FPM log.

Do my Apache .htaccess rules work with PHP-FPM?

No. .htaccess is specific to Apache; neither Nginx nor PHP-FPM reads this file. Redirect, access restriction and URL rewriting rules have to be moved into the Nginx configuration.

BYK Yazılım Support Team
This guide is written and regularly reviewed by the BYK Yazılım support team. Last updated: 4 October 2026.

Related guides

Let us talk about your website infrastructure

BYK Yazılım builds corporate websites. Write to us with any questions about your site.

Contact us Our corporate website service