“No input file specified” or “Primary script unknown” in the error log is one of the most frequently encountered issues in nginx+PHP. People on serverfault and in the #nginx IRC channel asks for help with this so often that this post is mostly to allow me to be lazy and not have to type up the same answer every time.
This is actually an error from PHP and due to display_errors being 0ff people will often just get a blank page with no output. In a typical setup PHP will then send the error to stderr or stdout and nginx will pick up on it and log it in the nginx error log file. Thus people spend a ton of time trying to figure out why nginx isn’t working.
The root cause of the error is that PHP cannot find the file nginx is telling it to look for, and there are a few common cases that causes this.
Wrong Path Sent to PHP
The most common reason at the time of writing happens because a user uses a horrible tutorial found via google instead of actually understanding nginx. Reading my nginx primer will equip you to actually solve this on your own but since this post is actually dedicated to the error I’ll cheat this once and allow you to be lazy by just giving you the full solution.
Nginx tells PHP about the file to execute via the SCRIPT_FILENAME fastcgi_param value. Most examples in the wiki should define this as $document_root$fastcgi_script_name. The horrible tutorials will often hard code the path value but this is not desirable as we don’t want to duplicate information and invite future screw ups. So you’ve gone with the $document_root$fastcgi_script_name option and suddenly it’s no longer working.
This happens because nginx has 3 levels of inheritance commonly referred to as blocks, these being http, server and location, each being a sub-block of the parent. Directives in nginx inherit downwards but never up or across, so if you define something in one location block it will never be applied in any other location block under any circumstance.
Typically users define their index and root directive in location / because a tutorial told them to. So when they then define SCRIPT_FILENAME using $document_root the root directive is not actually defined and thus the SCRIPT_FILENAME value becomes just the URI making PHP look at the root server dir.
The simple solution here is to just define the directive in your server block. (or http block even!) Generally the higher up your can define a directive the less duplicate directives you’ll need.
Incorrect File Permissions
Most people don’t really believe me when I tell them their file permissions are incorrect. They’re looking at the damn permissions and the PHP user can read the file just fine! Sadly, this shows a lack of understanding of Unix user permissions. Being able to read a file is not enough, a user must also be able to traverse to the file.
This effectively means that not only should the file have read permission, but the entire directory structure should have execute permission so that the PHP user can traverse the path. An example of this:
Say you have an index.php file in /var/www. /var/www/index.php must have read permission and both /var and /var/www must have execute permissions.
Using Alias and $document_root
It is fairly common to define SCRIPT_FILENAME as $document_root$fastcgi_script_filename; However, $document_root does not account for the alias directive and will thus result in an incorrect path being sent. It is preferable to define SCRIPT_FILENAME as such:
fastcgi_param SCRIPT_FILENAME $request_filename;
This accounts for alias and ensures proper path is sent. One note about this is that $request_filename does not account for fastcgi_index, but you only use this index if you send a request ending in / to PHP. If you use the standard PHP location ~ \.php$ then you will never use the fastcgi_index directive.
If your PHP lives in a chrooted environment that nginx does not, then they basically have 2 different roots and the file path that nginx reports to PHP will not resolve to the actual file. A simple example to make this obvious:
Lets say you request phpinfo.php, nginx looks in your defined root and finds this file in: /home/user/public_html/phpinfo.php
The PHP process for user is chrooted to only have access to his directory for security reasons, so as far as PHP knows the root of the server is at /home/user. Therefore when nginx reports the file path PHP will look in: /home/user/home/user/public_html/phpinfo.php.
Either make sure both nginx and PHP has the same chroot or make sure you rewrite your internal URI before you fastcgi pass to PHP.
Open Basedir Restriction
PHP has the option to limit file access to certain directories defined in your php.ini. Some distributions might preconfigure this option for their packaging system.
SCRIPT_NAME Versus SCRIPT_FILENAME
Fastcgi has two parameters which are quite similiar in name, make sure that you’re not confusing the SCRIPT_NAME variable for the SCRIPT_FILENAME one. SCRIPT_FILENAME tells fastcgi the location of the script to execute while SCRIPT_NAME merely tells the name of the script.