Fixing the Nginx 'Primary script unknown' Error with PHP-FPM

beginner Nginx2026-04-19| Linux (Ubuntu 22.04, Debian 12, CentOS 9, RHEL 9), Nginx 1.18+, PHP-FPM (7.4, 8.0, 8.1, 8.2, 8.3)

Error Message

FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream
#nginx#php-fpm#devops#troubleshooting#linux-server

The Anatomy of the Error

You hit your web application and see a cold "404 Not Found" page. The file exists on your disk, yet Nginx refuses to serve it. A quick look at your error logs at /var/log/nginx/error.log reveals a frustrating message:

FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream

Behind the Scenes

This error signals a communication breakdown between Nginx and PHP-FPM. Nginx acts as the gatekeeper, receiving the request and passing it to the PHP processor. However, PHP-FPM responds with a "not found" because it cannot locate the file path Nginx provided. Most often, this boils down to one of three configuration gaps.

  • The SCRIPT_FILENAME variable passed to PHP-FPM points to a non-existent path.
  • The root directive is trapped inside the wrong configuration block.
  • The PHP-FPM process lacks the necessary permissions to read the directory.

Step-by-Step Solutions

1. Audit the SCRIPT_FILENAME Parameter

The most frequent culprit is a misconfigured fastcgi_param SCRIPT_FILENAME. Open your site configuration file, typically located at /etc/nginx/sites-available/example.com.

Find your PHP location block. It should look similar to this:

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

The Fix: Ensure SCRIPT_FILENAME uses $document_root$fastcgi_script_name. If you hardcoded a path like /var/www/html$fastcgi_script_name, verify every character. A single typo in the folder name will trigger a 404.

2. Escape the Root Directive Trap

Misplacing the root directive is a common pitfall. If you define root inside a location / block, the PHP block won't inherit it. This leaves the $document_root variable empty when Nginx hands the request to PHP-FPM.

Avoid this mistake:

server {
    listen 80;
    server_name example.com;

    location / {
        root /var/www/my-app/public; # Wrong: Limited scope
        index index.php;
    }

    location ~ \.php$ {
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        ...
    }
}

The Correct Way: Move the root directive to the server level. This ensures it applies to every location block in the file.

server {
    listen 80;
    server_name example.com;
    root /var/www/my-app/public; # Correct: Global scope

    location / {
        index index.php;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    }
}

3. Resolve Permission Bottlenecks

PHP-FPM is picky about permissions. It usually runs as www-data or php-fpm, not as the user who uploaded the files. If the PHP worker cannot "traverse" the directory tree, it will report the script as unknown.

Identify your PHP user with this command:

ps aux | grep php-fpm

Verify that this user has read access to your PHP files and execute access to every parent directory. For a standard Ubuntu setup, you can reset ownership and permissions quickly:

sudo chown -R www-data:www-data /var/www/my-app
sudo find /var/www/my-app -type d -exec chmod 755 {} \;
sudo find /var/www/my-app -type f -exec chmod 644 {} \;

4. Tame SELinux on RHEL/CentOS

On Red Hat-based systems, SELinux is often the silent killer. It might block Nginx even if your Linux permissions are a perfect 777. Test this by temporarily switching to permissive mode:

sudo setenforce 0

If your site suddenly works, SELinux was the barrier. Restore enforcement and apply the correct security context to your web folder:

sudo setenforce 1
sudo chcon -Rt httpd_sys_content_t /var/www/my-app

Applying the Changes

Never restart Nginx without testing the syntax first. One missing semicolon can take your entire server offline.

sudo nginx -t

If the test passes, reload the services to apply your fixes:

sudo systemctl reload nginx
sudo systemctl restart php8.2-fpm

Refresh your browser. Your PHP application should now load correctly.

Pro-Tips for Troubleshooting

  • Watch out for Symlinks: If you use deployment tools like Capistrano, ensure Nginx is allowed to follow symbolic links. Set disable_symlinks off; in your config if needed.
  • Check Socket Paths: Verify that fastcgi_pass matches the actual socket file in /etc/php/8.2/fpm/pool.d/www.conf. A version mismatch (e.g., config says 8.1 but 8.2 is installed) is a frequent headache.
  • Document Root Precision: In your fastcgi_param, avoid trailing slashes if your root directive already includes one. Double slashes (e.g., /var/www//index.php) can occasionally confuse certain PHP-FPM versions.

Related Error Notes