The error means PHP-FPM cannot open the exact filesystem path Nginx sent in SCRIPT_FILENAME. Fix it by finding the winning Nginx location, calculating that parameter for the failing request, and checking the same path from the PHP-FPM worker’s filesystem and permissions view. “No such file or directory” normally indicates a bad path mapping, root, alias, URI split, or chroot. “Permission denied” means the worker cannot traverse a directory, read the script, or is blocked by a mandatory-access-control policy.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
What “unable to open primary script” actually means
Nginx does not execute a PHP file itself. It sends a FastCGI request to a PHP-FPM pool. Among the parameters in that request is SCRIPT_FILENAME, the absolute path PHP-FPM should open and execute. A common configuration constructs it as:
As an Amazon Associate I earn from qualifying purchases.
SCRIPT_FILENAME = $document_root$fastcgi_script_name
$document_root is the value of the active root directive. $fastcgi_script_name is derived from the request URI and can include an index filename when a directory URI is processed with fastcgi_index. If the resulting path is wrong, PHP-FPM reports that it cannot open the primary script even when a file with the same name exists somewhere else on the server.
#1 Best Overall
The path printed after Unable to open primary script: is the most useful clue. It is the path PHP-FPM received, not necessarily the path you intended and not necessarily the path visible from an interactive shell.
Start with the error suffix
| Suffix in the PHP-FPM log | What it usually means | First checks |
|---|---|---|
No such file or directory |
The path assembled by Nginx does not exist from the FPM worker’s view. | Check root, alias, regex locations, duplicate parameter includes, URI-to-file mapping, path-info splitting, and chroot paths. |
Permission denied |
The path may exist, but the FPM identity cannot traverse a parent directory or read the file, or a security policy denies access. | Check the pool’s user/group, execute permission on every parent directory, file read permission, and AppArmor, SELinux, or an equivalent policy. |
Do not change permissions blindly before checking the path. A permission change cannot repair a path that points to the wrong directory, and making an entire web tree world-writable creates a separate security problem.
Use this diagnostic sequence
- Capture the complete failing request. Record the URI, the path shown after
Unable to open primary script:, and the complete suffix. A request for/index.phpand a request for/app/index.php/foocan select different locations or produce different script paths. - Dump the effective Nginx configuration. Run
sudo nginx -Tand locate the server block and location that actually match the request. Also note every included file containingfastcgi_param,root,alias,fastcgi_split_path_info, orfastcgi_index. - Find the winning location, not just a nearby one. A regular-expression PHP location, an exact location, or a nested location can take precedence over the block you edited. Confirm which block handles the failing URI and whether it inherits or overrides the parameters you expect.
- Calculate
SCRIPT_FILENAMEby hand. Substitute the active document root and the actual FastCGI script name. Write the final absolute path down, including repeated prefixes or unexpected path-info segments. - Compare that path with the deployment layout. Use
stat,readlink -f, ornamei -lon the exact path. If the FPM pool useschroot, calculate the path inside the jail rather than using the host’s full path. - Test as the pool identity. Identify the
userandgroupin the active pool configuration, then test directory traversal and file readability as that identity. - Check mandatory access control. If Unix ownership and mode bits look correct but the suffix is
Permission denied, inspect the relevant AppArmor, SELinux, or platform audit logs. - Reject missing scripts before dispatch. Add an intentional
try_files $uri =404;check (or an equivalent regular-file check) in the PHP location, then verify that path-info and index routing still match the application. - Validate and retest. Run
sudo nginx -t, reload Nginx and the relevant PHP-FPM service, request a known existing PHP file, and check the error log again. A configuration change is not confirmed until the original URI succeeds and the old error no longer appears.
Inspect the effective Nginx mapping
Check root versus alias
root appends the request URI to a directory. With root /srv/www/site;, a request for /index.php normally maps to /srv/www/site/index.php. alias replaces the location prefix instead, so its trailing-slash form and the location pattern matter. Treating an alias as if it were a root can add or remove a directory component and produce an apparently inexplicable “file not found” message.
When a PHP location is nested below an aliased location, verify the value of $document_root and whether the FastCGI parameter was written for that layout. If the application is mounted at a URI prefix, calculate the mapping with the exact prefix removed or retained as Nginx does; do not infer it from the browser URL alone.
Look for overridden or duplicated FastCGI parameters
Nginx inherits fastcgi_param directives only when none are defined at the current configuration level. A second include, or a location-level fastcgi_param, can therefore replace the value you thought was active. Inspect the complete output of nginx -T rather than assuming that the commonly included parameter file is the final authority.
Pay particular attention to configurations that include one parameter file globally and another inside the PHP location. A stale hard-coded SCRIPT_FILENAME can silently override a correct variable-based line, while a location-level value can prevent an inherited value from being used at all.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #2
Calculate the path PHP-FPM receives
Simple request example
Suppose the active root is /var/www/example/public and the request is /index.php. If the FastCGI script name is /index.php, the standard expression produces:
/var/www/example/public/index.php
Compare that exact string with the file’s real path. Differences such as /public/public/index.php, a missing release directory, or a path under the wrong virtual host identify the mapping error immediately.
Directory requests and fastcgi_index
For a URI ending in /, fastcgi_index can add an index filename to $fastcgi_script_name. A request for / might therefore become /index.php, while a request for /admin/ might become /admin/index.php. Confirm that the configured index file exists under the active root and that it is the file the application expects.
Path information
A URI such as /index.php/account contains a script and an additional path-info component. If the configuration uses fastcgi_split_path_info, verify which part becomes SCRIPT_FILENAME and which part becomes PATH_INFO. If the split is absent or incorrectly ordered, PHP-FPM may be asked to open a literal path ending in /account instead of the real index.php file. Enable path-info handling only when the application requires it; a plain .php$ location is safer for sites that serve only direct PHP files.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use a safe baseline for a non-chroot pool
For a conventional deployment in which Nginx and PHP-FPM see the same host filesystem, start from a minimal mapping and adapt only the socket and root:
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 must match the active PHP-FPM pool. The important part for this error is that the absolute prefix in SCRIPT_FILENAME resolves to the same filesystem location PHP-FPM can access. A fixed path can be used instead:
fastcgi_param SCRIPT_FILENAME /var/www/example/public$fastcgi_script_name;
A hard-coded prefix can be appropriate when the virtual host has one immutable document root, but it becomes dangerous when copied between sites, releases, containers, or server blocks. A variable-based expression is easier to reuse only when the active root truly represents the PHP-FPM view.
Why try_files belongs before fastcgi_pass
try_files $uri =404; checks that the requested file exists before Nginx opens a FastCGI connection. Typos, probes for deleted files, and stale links then receive a normal 404 instead of producing an FPM “primary script” error. This check does not fix a wrong root or permissions; it simply prevents nonexistent requests from being dispatched as PHP scripts. Keep it only when the location’s routing model supports it, and test path-info applications separately.
Fix a chroot mismatch
PHP-FPM’s pool-level chroot changes the worker’s filesystem root when workers start. The host path before the jail is invisible from inside it. PHP-FPM’s chdir setting changes the worker’s current directory but does not create the same filesystem view as chroot.
How the doubled path happens
Assume the host stores the script at /var/www/site/index.php and the pool contains:
chroot = /var/www/site
Inside the jail, that same file is simply /index.php. If Nginx sends /var/www/site/index.php, PHP-FPM interprets it relative to the jail and looks for:
/var/www/site/var/www/site/index.php
That path does not exist, so the log reports “No such file or directory.” The correction is to send a path that exists inside the jail, such as $fastcgi_script_name when it resolves to /index.php, or another deliberately constructed jail-relative path. The exact expression depends on the URI layout; do not prepend the host-side chroot directory a second time.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Rank #4
Make every pool path chroot-consistent
After correcting SCRIPT_FILENAME, inspect the rest of the pool configuration. The socket, access and error logs, temporary upload directory, session storage, working directory, and any referenced include or extension paths must exist in the worker’s visible filesystem. A script may begin executing while uploads or sessions still fail because those paths were specified only from the host’s perspective.
If Nginx is outside the jail and PHP-FPM is inside it, keep the two views explicit in your configuration notes. The path Nginx uses to locate a file is not automatically the path PHP-FPM must use to open it.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Resolve “Permission denied” without over-permissioning
Identify the actual pool identity
Read the active pool file and record its user and group. Do not assume they are the same account Nginx uses. A distribution may run Nginx and PHP-FPM under different identities, and separate pools may use different users.
Check every directory component
The worker needs execute (x) permission on every parent directory in the path, plus read (r) permission on the PHP file. Inspect the full chain with:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutenamei -l /var/www/example/public/index.php
stat /var/www/example/public/index.php
For a directory, execute permission controls traversal; read permission controls listing. A directory can therefore be traversable without being listable, which is sufficient for opening a known script. Avoid granting broad permissions to the whole tree when a group ownership or ACL change on the application directories is the narrower fix.
Test as the worker
After confirming the pool user, test readability and traversal as that account. On systems using the common web-service identity, the pattern is:
Best Value
sudo -u www-data test -r /var/www/example/public/index.php && echo readable
sudo -u www-data namei -l /var/www/example/public/index.php
Replace www-data with the configured pool user. If the test fails, correct ownership, group membership, ACLs, or directory modes at the narrowest level that permits the application to run.
Check mandatory access control
When mode bits and ownership permit access but PHP-FPM still logs Permission denied, inspect the operating system’s security audit records. AppArmor profiles, SELinux labels and policies, or equivalent controls can deny access even when ordinary Unix checks succeed. The audit event normally identifies the executable, path, and policy rule that needs adjustment. Change the policy deliberately and retest; disabling the protection globally is not a reliable fix.
Recommended Free Tools
Common configuration failures and their corrections
| Failure pattern | Observed result | Correction |
|---|---|---|
| Document root points at the project parent instead of its public directory. | PHP-FPM receives a path one directory above or below the real script. | Set the server or location root to the directory that actually contains the public PHP entry point, then recalculate the parameter. |
| An alias location is treated like a root location. | The URI prefix is duplicated or omitted. | Map the alias deliberately and use a FastCGI filename expression that matches the resulting filesystem path. |
| A stale included parameter overrides the intended line. | Editing one file has no effect; the log continues to show the old path. | Use nginx -T to find every active SCRIPT_FILENAME and remove or correct the overriding directive. |
| The host path is sent to a chrooted pool. | The error path contains the chroot prefix twice or points outside the jail. | Send the path as seen inside the jail and make pool support paths jail-consistent. |
| Path-info splitting is enabled for a plain-file site. | PHP-FPM tries to open a path containing the extra URI segment. | Remove unnecessary splitting, or configure the split so only the real script becomes SCRIPT_FILENAME. |
| A directory request adds an unexpected index file. | The error names an index.php that is absent from that directory. |
Check fastcgi_index, the application’s directory layout, and whether the request should be routed elsewhere. |
| The script exists but a parent directory is not traversable. | The suffix is Permission denied. |
Grant the pool identity execute permission on each parent, then check mandatory access-control policy. |
Security and operational checks after the fix
Keep PHP-FPM on a protected Unix socket or a restricted local endpoint. The PHP documentation warns that a client able to open a FastCGI connection can influence request configuration, including auto_prepend_file, and may be able to execute arbitrary code. Do not expose an unrestricted FPM socket or TCP listener to an untrusted network.
Do not accept a “fix” that merely changes the error to a different failure. Confirm all of the following:
sudo nginx -treports a successful configuration test.- Nginx and the relevant PHP-FPM service were reloaded after the change.
- A known existing PHP script returns the expected application response.
- A nonexistent PHP path returns a controlled 404 when
try_filesis enabled. - The Nginx and PHP-FPM logs no longer contain the original path error.
- Uploads, sessions, logs, and other pool-specific paths still work, especially after a chroot change.
If the error returns only after a deployment, compare the release symlink, document root, and pool reload timing. A new release directory can exist on the host while the running FPM workers still have an old chroot or stale path configuration.
Quick Recap
A compact decision path
- Read the exact path and suffix in the log.
- Use
nginx -Tto identify the matching location and every active FastCGI parameter. - Compute
SCRIPT_FILENAMEfor the failing URI, including index and path-info rules. - Check that path from the FPM worker’s view; account for
chroot. - For “No such file or directory,” repair mapping or jail-relative paths.
- For “Permission denied,” test the pool identity, parent-directory traversal, file readability, and mandatory access control.
- Add or verify
try_files $uri =404;where the application’s routing permits it. - Test, reload, request both an existing and a missing script, and inspect the logs.
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.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →




