Pairing Nginx with PHP-FPM on Fedora gives PHP applications a fast front end for static files, routing, and TLS while a separate FastCGI worker pool executes PHP code. To configure Nginx with PHP-FPM on Fedora, use Fedora’s packaged socket integration first, then add a site-specific server block when the default test page is not enough.
Fedora’s PHP-FPM package keeps the default pool under an apache service account, but that does not mean Apache HTTP Server is required. The packaged socket access control list already grants the nginx user access, so most setups should verify the default integration before changing pool ownership.
Install and Verify Required Packages
If Nginx and PHP-FPM are not installed yet, install the Fedora packages with DNF. For a longer package walkthrough, use the companion guides to install Nginx on Fedora and install PHP on Fedora.
sudo dnf install nginx php-fpm
Check that both packages are present. This is the quickest answer to “check if PHP-FPM is installed” on Fedora because it confirms the RPM packages, not only a command in your shell path.
rpm -q nginx php-fpm
Relevant output includes package names and Fedora build numbers:
nginx-1.30.0-1.fc44.x86_64 php-fpm-8.5.6-1.fc44.x86_64
Confirm the service binaries respond as expected:
nginx -v
php-fpm -v
Version numbers change as Fedora publishes updates, but the output should identify Nginx and PHP-FPM:
nginx version: nginx/1.30.0 PHP 8.5.6 (fpm-fcgi)
Understand How Nginx and PHP-FPM Work Together
Nginx and PHP-FPM do different jobs. Nginx accepts HTTP requests, serves static files, and decides which requests need PHP processing. PHP-FPM listens separately and runs PHP scripts through a worker pool. The Nginx FastCGI configuration connects those two pieces by sending PHP requests to the PHP-FPM socket.
- Nginx: Handles the client connection, static files, virtual host routing, and reverse-proxy style request handling.
- PHP-FPM: Runs PHP code through managed worker processes instead of embedding PHP inside the web server.
- FastCGI socket: Provides the local handoff point between Nginx and PHP-FPM, normally
/run/php-fpm/www.sockon Fedora.
Inspect Fedora’s Default PHP-FPM Socket
The main PHP-FPM pool file is /etc/php-fpm.d/www.conf. Start with a read-only check so you can confirm the active pool and socket lines without changing the file. The grep command in Linux filters the file to the directives that matter for this connection.
grep -E '^(user|group|listen(\.| =))' /etc/php-fpm.d/www.conf
A Fedora 44 package install currently reports these lines:
user = apache group = apache listen = /run/php-fpm/www.sock listen.acl_users = apache,nginx listen.allowed_clients = 127.0.0.1
The important line is listen.acl_users = apache,nginx. It gives the nginx service account permission to connect to the PHP-FPM Unix socket while PHP scripts still run as the Fedora package’s default apache worker account.
Open the pool file only when you intentionally need to change pool settings:
sudo nano /etc/php-fpm.d/www.conf
Do not change
userandgroupjust because the account is namedapache. Change the pool user only when your site’s file ownership model requires it, then update writable directories and SELinux labels to match the new worker account.
After PHP-FPM starts, verify that the socket exists and that Nginx has access through an ACL entry. If getfacl is not found, install the acl package.
sudo systemctl start php-fpm
ls -l /run/php-fpm/www.sock
getfacl -p /run/php-fpm/www.sock
In the ls output, the socket mode should end with a +, which marks extended ACLs. In the getfacl output, look for this Nginx ACL entry:
user:nginx:rw-
Inspect Fedora’s Nginx PHP-FPM Snippets
Fedora’s Nginx package includes a PHP-FPM upstream definition under /etc/nginx/conf.d/ and a reusable PHP location snippet under /etc/nginx/default.d/. The upstream definition should point at the same Unix socket that PHP-FPM creates:
sudo cat /etc/nginx/conf.d/php-fpm.conf
# PHP-FPM FastCGI server
# network or unix domain socket configuration
upstream php-fpm {
server unix:/run/php-fpm/www.sock;
}
Review the packaged PHP handler as well:
sudo cat /etc/nginx/default.d/php.conf
index index.php index.html index.htm;
location ~ \.(php|phar)(/.*)?$ {
fastcgi_split_path_info ^(.+\.(?:php|phar))(/.*)$;
fastcgi_intercept_errors on;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php-fpm;
}
The default Fedora Nginx server includes files from /etc/nginx/default.d/*.conf, so the stock test site can process PHP after both services are running. Custom server blocks need their own PHP location block or an explicit include for the packaged snippet.
Start and Restart Nginx and PHP-FPM
Enable both services to start at boot and start them now:
sudo systemctl enable --now php-fpm nginx
Check the runtime state with the narrow service probe. A healthy pair returns one active line per service:
systemctl is-active php-fpm nginx
active active
Restart PHP-FPM after changing pool settings or PHP extensions. Reload Nginx after a successful syntax check when only Nginx configuration changed:
sudo systemctl restart php-fpm
sudo nginx -t && sudo systemctl reload nginx
Test PHP-FPM Through the Default Nginx Site
Run the Nginx syntax check before testing a browser request:
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Create a small PHP test file in Fedora’s default Nginx web root. The sudo tee pattern writes to a root-owned path, while > /dev/null hides the echoed file body from your terminal.
sudo tee /usr/share/nginx/html/lc-php-test.php > /dev/null <<'EOF'
<?php echo "PHP-FPM OK\n"; ?>
EOF
Probe the local response with the curl command in Linux. The -f flag fails on HTTP errors, and -sS keeps normal output quiet while still showing errors.
curl -fsS http://127.0.0.1/lc-php-test.php
PHP-FPM OK
Remove the test file after the check so the default site does not expose a leftover diagnostic endpoint:
sudo rm -f /usr/share/nginx/html/lc-php-test.php
Create a Custom Nginx Server Block for PHP-FPM
Production PHP sites usually use their own document root, access log, error log, and server name. Fedora’s Nginx package loads custom virtual host files from /etc/nginx/conf.d/*.conf, so place one file there for each site.
Create the document root and add a temporary PHP index file for the first response test:
sudo mkdir -p /var/www/example.com/html
sudo tee /var/www/example.com/html/index.php > /dev/null <<'EOF'
<?php echo "custom PHP-FPM OK\n"; ?>
EOF
Install the SELinux management tool if semanage is not already available, then label the site path as web content. The conditional keeps the command reusable: it updates the rule if it already exists and creates it when it does not.
sudo dnf install policycoreutils-python-utils
if sudo semanage fcontext -l | grep -Fq '/var/www/example.com(/.*)?'; then
sudo semanage fcontext -m -t httpd_sys_content_t "/var/www/example.com(/.*)?"
else
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/example.com(/.*)?"
fi
sudo restorecon -Rv /var/www/example.com
Create the server block, replacing example.com with your real domain:
sudo nano /etc/nginx/conf.d/example.com.conf
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.php index.html index.htm;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.(php|phar)(/.*)?$ {
fastcgi_split_path_info ^(.+\.(?:php|phar))(/.*)$;
try_files $fastcgi_script_name =404;
fastcgi_intercept_errors on;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php-fpm;
}
location ~ /\.ht {
deny all;
}
}
This server block keeps application routing in the root location, passes only existing PHP or PHAR files to PHP-FPM, and uses Fedora’s packaged php-fpm upstream. The try_files $fastcgi_script_name =404 line prevents non-existent PHP paths from being handed to PHP-FPM.
Test the configuration, reload Nginx, and probe the virtual host locally before changing DNS or public firewall rules. The --resolve option maps example.com to loopback for this one request, so the check reaches the named server block even before public DNS points at the server.
sudo nginx -t
sudo systemctl reload nginx
sleep 1
curl -fsS --resolve example.com:80:127.0.0.1 http://example.com/
custom PHP-FPM OK
Allow PHP to Write to Application Directories
Keep most web files read-only to the PHP worker. When an application needs writable directories, such as framework cache folders or upload paths, assign those specific directories to the PHP-FPM worker account shown in /etc/php-fpm.d/www.conf. With Fedora’s default pool, that account is apache.
sudo mkdir -p /var/www/example.com/html/{storage,cache}
sudo chown -R apache:apache /var/www/example.com/html/storage /var/www/example.com/html/cache
if sudo semanage fcontext -l | grep -Fq '/var/www/example.com/html/(storage|cache)(/.*)?'; then
sudo semanage fcontext -m -t httpd_sys_rw_content_t "/var/www/example.com/html/(storage|cache)(/.*)?"
else
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/example.com/html/(storage|cache)(/.*)?"
fi
sudo restorecon -Rv /var/www/example.com/html/storage /var/www/example.com/html/cache
If you intentionally changed the PHP-FPM pool to run as nginx, use nginx:nginx for those writable directories instead. Keep the ownership model consistent with the worker account, not with the public-facing web server by habit.
Open Firewall Access for HTTP and HTTPS
Fedora uses firewalld by default on standard installations. If the server needs to accept traffic from other machines, confirm firewalld is running and allow the HTTP and HTTPS service definitions. For a broader firewall walkthrough, see how to install and configure firewalld on Fedora.
sudo firewall-cmd --state
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Verify that both services are allowed in the active zone:
sudo firewall-cmd --query-service=http
sudo firewall-cmd --query-service=https
yes yes
Handle SELinux for PHP-FPM Applications
Fedora enables SELinux in enforcing mode by default. The standard Nginx and PHP-FPM package paths work with the default policy, but custom document roots, writable directories, outbound API calls, database connections, or mail delivery may require specific SELinux labels or booleans.
Check the common HTTPD booleans before enabling anything:
getsebool httpd_can_network_connect httpd_can_network_connect_db httpd_can_sendmail
httpd_can_network_connect --> off httpd_can_network_connect_db --> off httpd_can_sendmail --> off
Run only the command that matches the access your application actually needs:
# External APIs or other outbound network calls
sudo setsebool -P httpd_can_network_connect on
# Network database connections
sudo setsebool -P httpd_can_network_connect_db on
# Local sendmail access
sudo setsebool -P httpd_can_sendmail on
Each SELinux boolean widens what web server processes can do. Enable the network, database, or mail boolean only after the application needs that access and a log check confirms SELinux is the blocking layer.
When requests fail with permission messages, inspect recent AVC denials. Install audit first if the command is missing:
sudo dnf install audit
sudo ausearch -m avc -ts recent | grep -E 'nginx|php-fpm|httpd_t' | tail
Prefer the narrow fix first: restore the file context, label a custom path, or enable the specific boolean. Avoid generating a broad local policy until you understand the denial and have ruled out path labels and standard booleans.
Troubleshoot Nginx and PHP-FPM Errors
Fix 502 Bad Gateway from PHP-FPM
A 502 response usually means Nginx cannot reach PHP-FPM or PHP-FPM rejected the FastCGI handoff. Start with the service state and socket permissions:
systemctl is-active php-fpm nginx
ls -l /run/php-fpm/www.sock
getfacl -p /run/php-fpm/www.sock
The service check should return active for both units, and the ACL output should include user:nginx:rw-. If the socket is missing, restart PHP-FPM. If the ACL entry is missing, recheck listen.acl_users in /etc/php-fpm.d/www.conf and restart PHP-FPM after correcting it. For a wider Nginx diagnostic path, use the fix Nginx 502 Bad Gateway guide.
Fix PHP Downloading as Text
If the browser downloads PHP files or displays raw PHP source, Nginx is serving the file as static content instead of passing it to PHP-FPM. Confirm that the active configuration includes a PHP location block and the fastcgi_pass php-fpm; directive:
sudo nginx -T | grep -E 'default.d/php.conf|fastcgi_pass|php-fpm'
If your custom server block does not include the packaged snippet, copy the PHP location block from this article into that server block, then run sudo nginx -t and reload Nginx.
Fix File Not Found or Blank PHP Pages
A 404, blank page, or PHP-FPM “Primary script unknown” entry usually points to the wrong document root, missing file, or bad SCRIPT_FILENAME value. Check the site file, PHP-FPM log, and Nginx error log together. The tail command in Linux keeps each log read focused on recent entries.
sudo nginx -T | grep -E 'server_name|root |SCRIPT_FILENAME'
sudo tail -n 40 /var/log/php-fpm/www-error.log
sudo tail -n 40 /var/log/nginx/example.com.error.log
The root directive must point to the directory that contains the PHP file, and SCRIPT_FILENAME should use $document_root$fastcgi_script_name. If Nginx returns a plain 404 for every route, review the site-specific routing pattern or use the fix Nginx 404 Not Found checklist.
Fix SELinux Permission Denials
If logs show “Permission denied” and normal Unix ownership looks correct, restore labels on the document root and check recent SELinux denials:
sudo restorecon -Rv /var/www/example.com
sudo ausearch -m avc -ts recent | tail -20
For custom writable directories, use httpd_sys_rw_content_t only on the paths PHP must write to. Do not label the whole site writable unless the application truly needs that broad access.
Remove the Example Site or PHP-FPM Stack
Remove only the example virtual host when you are keeping Nginx and PHP-FPM for other sites:
The cleanup commands delete the example server block and its document root. Replace
example.comwith the real test path, and do not run the recursive remove command against a production site that contains data you need.
sudo rm -f /etc/nginx/conf.d/example.com.conf
sudo rm -rf /var/www/example.com
sudo semanage fcontext -d "/var/www/example.com/html/(storage|cache)(/.*)?" 2>/dev/null || true
sudo semanage fcontext -d "/var/www/example.com(/.*)?" 2>/dev/null || true
sudo nginx -t && sudo systemctl reload nginx
If this was only a temporary lab server and you want to remove the web stack as well, stop the services first, then remove the packages:
sudo systemctl disable --now nginx php-fpm
sudo dnf remove nginx php-fpm
If you opened HTTP and HTTPS only for this temporary stack, close those firewalld services after removing the site:
sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --permanent --remove-service=https
sudo firewall-cmd --reload
If you enabled SELinux booleans only for this application, turn those specific booleans off after confirming no other web application still needs them:
sudo setsebool -P httpd_can_network_connect off
sudo setsebool -P httpd_can_network_connect_db off
sudo setsebool -P httpd_can_sendmail off
DNF may preserve modified configuration files as RPM save files. Review any remaining files under /etc/nginx/, /etc/php-fpm.d/, and your web roots before deleting them manually.
Conclusion
Nginx now passes PHP requests to PHP-FPM through Fedora’s packaged Unix socket, with service checks, a site-specific server block, SELinux labels, and rollback commands in place. From here, harden the virtual host with configure security headers in Nginx or improve response size with enable gzip compression in Nginx before exposing the site to real traffic.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><blockquote>quote</blockquote>