October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

Fix Nginx PHP-FPM “Unable to Open Primary Script”

Learn why PHP-FPM cannot open a script and fix wrong SCRIPT_FILENAME mappings, chroot mismatches, permissions, aliases, PATH_INFO, and missing-file handling.
Length8 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“Unable to open primary script” means PHP-FPM received a filesystem path that it cannot open. In most cases, Nginx built the wrong SCRIPT_FILENAME from the request URI and its root/alias; in other cases, the path is correct on the host but invisible or unreadable to the PHP-FPM worker. Read the error suffix, calculate the exact path, then test that path from the pool’s filesystem and security context.

What PHP-FPM is trying to open

Nginx sends PHP-FPM a FastCGI parameter named SCRIPT_FILENAME. PHP uses that parameter as the script’s absolute filename. A common configuration constructs it as:

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

$document_root$fastcgi_script_name

$document_root is the value supplied by the active Nginx root directive. $fastcgi_script_name is derived from the request URI. For a request to /index.php under root /var/www/site;, the resulting filename should be /var/www/site/index.php.

If the resulting path is wrong, missing, outside a chroot, or blocked by permissions, PHP-FPM reports the failure. The path printed after Unable to open primary script: is therefore the most useful clue; it is the path PHP-FPM actually received, not merely the URL the browser requested.

Read the error suffix before changing configuration

Error ending What it usually means First checks
No such file or directory The generated path does not exist in the worker’s filesystem view. Active root/alias, duplicate fastcgi_param includes, URI-to-file mapping, fastcgi_index, and chroot paths.
Permission denied The path exists, but the FPM worker cannot traverse a directory, read the script, or pass a mandatory access-control policy. Pool identity, directory execute bits, file read bits, ACLs, SELinux/AppArmor, and the exact path shown in the log.

Do not treat both messages as the same problem. Adding a file when the suffix is Permission denied will not help, and changing permissions will not repair a path that contains an extra directory prefix.

A diagnostic sequence that finds the real cause

  1. Capture the complete request and filename

    Copy the request URI, the complete Nginx error-log line, and its final operating-system message. Record whether the request was for a plain script such as /index.php, a directory such as /, or a path containing additional information such as /index.php/foo. The difference determines how $fastcgi_script_name is formed.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  2. Inspect the location Nginx actually selected

    Review the server block and every included file. Pay particular attention to regex PHP locations, nested locations, alias, and snippets that contain FastCGI parameters. Run nginx -T and inspect the complete expanded configuration, not just the file you recently edited. A location-level fastcgi_param changes the effective value, and Nginx inherits FastCGI parameters only when none are defined at the current level. Two parameter includes can therefore leave a different SCRIPT_FILENAME than the one you intended.

  3. Calculate the filename by hand

    Write down the active document root and the exact $fastcgi_script_name for the request, then concatenate them. For /index.php with /var/www/site, the expected result is /var/www/site/index.php. If the log shows /var/www/site/var/www/site/index.php, a host path has been prefixed twice. If it shows a directory or an unexpected application subdirectory, the selected location or alias is wrong.

  4. Test the path from PHP-FPM’s view

    Confirm that the exact filename exists where the worker can see it. A shell check such as ls -l /var/www/site/index.php reflects the host namespace. It does not prove that a pool with chroot enabled sees that same path. Compare the host path with the path inside the jail, and account for containers or other filesystem namespaces as well.

  5. Check the pool identity and access policy

    Read the pool configuration for user and group. That identity must be able to execute every parent directory and read the PHP file. If the suffix is Permission denied, also check ACLs and mandatory controls such as SELinux or AppArmor; a normal Unix mode listing can look correct while policy still denies the open.

    What’s actually slowing this PC down?

    Pick the symptom - the matching free tool is one click away.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  6. Reject missing scripts before FastCGI dispatch

    Add a file-existence check such as try_files $uri =404; to the PHP location, or an equivalent -f test. A nonexistent script should become a normal 404 rather than being sent to PHP-FPM. After changing routing, retest both an existing script and a deliberately missing one.

Build and verify SCRIPT_FILENAME correctly

Standard document-root layout without chroot

For a conventional site whose Nginx and PHP-FPM processes share the host filesystem, use one clear mapping:

location ~ .php$ {
    try_files $uri =404;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php-fpm.sock;
}

The socket path is distribution- and pool-specific; use the socket configured by your PHP-FPM service. The important part is that the prefix in SCRIPT_FILENAME resolves to the same absolute path that the FPM worker can open.

A hard-coded path can be equally valid when the virtual host has one fixed deployment directory:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
fastcgi_param SCRIPT_FILENAME /var/www/site$fastcgi_script_name;

Do not combine a hard-coded directory with a $document_root that already contains that directory. That creates the duplicated-prefix failure visible in the log.

Why root and alias are not interchangeable

With root, Nginx appends the request URI to the root directory. With alias, Nginx replaces the matching location prefix with the alias path. A PHP location designed around $document_root$fastcgi_script_name can therefore point to the wrong place when the request is served from an aliased location.

For an aliased application, calculate the replacement explicitly and use a path that matches the alias’s file layout. Keep the PHP handler inside the correct location, and verify the resulting filename in the error log or with a controlled test request. Do not assume that changing root to alias leaves the FastCGI expression unchanged.

Directory requests and fastcgi_index

When a URI ends in /, Nginx can append the filename configured by fastcgi_index while forming $fastcgi_script_name. A request for / may therefore become /index.php, while a request for another directory may produce a different index path. Make sure the configured index file exists under the active root and that the application’s front controller is actually named that way.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Chroot: the most common “file exists” contradiction

PHP-FPM’s chroot changes the root directory seen by workers at startup. If a pool has chroot = /var/www/site, the host file /var/www/site/index.php is visible to that worker as /index.php. Passing the host path to PHP-FPM makes it search for /var/www/site/var/www/site/index.php inside the jail, which does not exist.

Host filesystem Path inside chroot = /var/www/site Value PHP-FPM should receive
/var/www/site/index.php /index.php /index.php
/var/www/site/public/index.php /public/index.php /public/index.php

In a chrooted pool, pass the path that exists inside the jail, often the chroot-relative $fastcgi_script_name, rather than prepending the host directory. The exact expression depends on your Nginx root and application layout; derive it from the worker’s view instead of copying a no-chroot example.

Chroot affects more than the script. The pool’s socket, logs, temporary directories, session storage, working directory, and any files loaded by the application must be reachable at paths valid inside the jail. A corrected script path can expose the next missing in-jail directory if those supporting paths were left as host-absolute locations.

Fixing Permission denied

Check every directory in the path

To open /var/www/site/public/index.php, the FPM identity needs directory execute (x) permission on /var, /var/www, /var/www/site, and /var/www/site/public, plus read permission on the file. A readable file is still inaccessible when one parent directory blocks traversal.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use namei -l /var/www/site/public/index.php where available to inspect each component. Then verify the pool identity with a targeted access test, for example:

sudo -u www-data test -r /var/www/site/public/index.php && echo readable
sudo -u www-data test -x /var/www/site/public && echo traversable

Replace www-data with the actual user configured for the pool. Do not solve the problem by making the entire tree world-writable; grant the minimum traversal and read access required by the deployment.

Check ACLs and mandatory access control

POSIX ACLs can override what a basic mode listing suggests, so inspect them with getfacl when permissions look correct but access still fails. On systems enforcing SELinux, inspect audit events and apply a policy-compatible file context rather than disabling enforcement. On AppArmor systems, review the profile denial for the PHP-FPM process and allow only the required application paths. The exact error suffix and the security log together distinguish an operating-system permission problem from a policy denial.

Handle PATH_INFO and front-controller routing deliberately

A location matching every URI that ends in .php does not automatically handle /index.php/something correctly. If your application uses PATH_INFO, configure path splitting intentionally, ensure the script portion is the file you expect, and apply try_files to that script rather than blindly accepting arbitrary suffixes. If the application does not use PATH_INFO, reject or reroute those requests instead of allowing an accidental filename calculation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Test these cases separately:

  • /index.php — the direct script.
  • / — index handling and fastcgi_index.
  • /missing.php — should return 404 before PHP-FPM.
  • /index.php/extra — only if PATH_INFO is a documented application feature.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Use a controlled reload and verification cycle

  1. Save the current Nginx server block and PHP-FPM pool configuration so you can revert a failed change.

  2. Run nginx -t. Do not reload if the syntax or included-file test fails.

  3. Reload Nginx and restart or reload the affected PHP-FPM service when its pool configuration changed. Use the service name installed on your system.

  4. Request an existing PHP file and inspect both the HTTP response and the Nginx/PHP-FPM logs. A successful response alone is not sufficient if the request was served from a different location than the failing one.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  5. Request a known missing PHP filename and confirm that Nginx returns 404 without an FPM “Unable to open primary script” entry.

  6. Repeat the test for directory, aliased, PATH_INFO, and chrooted routes used by the application.

Only declare the issue fixed after the exact failing URL works and the error log no longer records the original path failure.

Fast reference: choose the correction by symptom

Observed symptom Likely correction
Log path contains the site root twice Remove the duplicate prefix; use either $document_root$fastcgi_script_name or one hard-coded absolute prefix, not both.
Log path points to the wrong virtual host directory Inspect the winning location, server name, regex precedence, and included fastcgi_param definitions.
File exists on host but not for a chrooted pool Use the chroot-relative path and make socket, logs, temporary files, sessions, and working directory valid inside the jail.
Existing file returns Permission denied Check pool user/group, parent-directory execute permission, file read permission, ACLs, and SELinux/AppArmor.
Missing scripts reach PHP-FPM Add try_files $uri =404; or an equivalent file test before fastcgi_pass.
Only directory or PATH_INFO URLs fail Check fastcgi_index, path splitting, and whether the generated script name matches the application layout.

Keep the FastCGI endpoint protected

Use a restricted local Unix socket or a protected local endpoint for PHP-FPM. A client that can open a FastCGI connection can influence request configuration, including auto_prepend_file, and may be able to execute arbitrary code. Do not expose the FPM socket or port to untrusted networks while troubleshooting the script path.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The durable fix is the simplest mapping that matches the FPM worker’s filesystem view: one authoritative root or alias, one intentional SCRIPT_FILENAME, an existence check before dispatch, and permissions that allow only the pool identity to read the application files.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.