Why are my CGI scripts not executed like PHP ?

Francis Daly francis at daoine.org
Sat Apr 7 14:18:10 UTC 2018


On Fri, Apr 06, 2018 at 09:53:27PM +0200, Ralph Seichter wrote:

Hi there,

This mail is a bit long, but I try to cover the points raised in your
previous mails too.

"CGI" is an interface between the executable (that you write or find;
commonly referred to as a "CGI script", although it may not be a
script) and the thing that executes it (typically, traditionally, a web
server). The CGI script expects to be run in a particular environment,
with particular environment variables set. It is expected to produce
output in a particular format.

Nginx does not "do" CGI.

FastCGI is a separate protocol. It defines the communication between the
client (typically, a web server) and the fastcgi server. What the server
does next is up to it; all the client cares about is that the response
is correctly formatted.

Nginx does "do" FastCGI; it knows how to act as a client talking to a
FastCGI server.

One FastCGI server is php-fpm. It executes PHP scripts. Whether it
provides a CGI-like environment and only executes PHP CGI scripts;
or whether it does its own magic to execute any PHP script, is not
something that the FastCGI client has to care about.

One FastCGI server is fcgiwrap. It is intended as a generic wrapper
around any CGI script. Fcgiwrap is intended to receive a FastCGI-protocol
request, executes a particular CGI script using the correct interface
(environment, input, output), accept the output, and return it
appropriately modified to the FastCGI client.


While nginx does speak the FastCGI protocol, and does include the
"generic" parameters (key/value pairs, effectively) in the communication,
it cannot know the full set of parameters that *this* FastCGI server
expects, or the particular values that some parameters should have for
*this* request. That's where the person configuring nginx comes in --
it is their responsibility to ensure that the nginx-side configuration
is appropriate.

I said that fcgiwrap "executes a particular CGI script". How does
the FastCGI server know which script that is? That is entirely up
to the FastCGI server to decide. Typically, it will use the value of
the parameter SCRIPT_FILENAME that is given to it. But maybe your one
does something else. Only you can know, based on the documentation or
implementation of your FastCGI server.

What happens if the client sends more than one value for the parameter
SCRIPT_FILENAME? Again, that is entirely up to the FastCGI server to
decide. Perhaps it uses the first; perhaps it uses the last; perhaps it
uses any of them randomly; perhaps it uses none.

What should the client (in this case: nginx) do if it is configured
to send more than one value for the parameter SCRIPT_FILENAME (or:
for any parameter)? It could try to be clever, and only send the first
value it is configured to send. Or only the last. Or only one of them,
randomly. Or it could assume that the administrator knows what they are
doing, and send whatever it is configured to send. Nginx does the latter.


So, with all that out of the way: what is the problem that you are
reporting?

You have an executable CGI script, /tmp/script, with the contents

==
#!/bin/sh
echo Content-Type: text/plain
echo
echo The script is running.
echo The environment is:
env
==

You want nginx to tell fcgiwrap to execute that script for all incoming requests:

==
server {
  listen 8008;
  location / {
    fastcgi_pass unix:/tmp/fcgi.sock;
	fastcgi_param SCRIPT_FILENAME /tmp/script;
  }
}
==

For this to work, you must have already configured a FastCGI-wrapper
to listen on /tmp/fcgi.sock and to use the parameter SCRIPT_FILENAME as
the name of the program to execute.

> I altered my setup to use fcgiwrap. Since then, I keep getting "502 Bad
> Gateway" errors, with log entries like this:
> 
>   2018/04/06 21:21:02 [error] 17838#0: *1 upstream prematurely closed
>   FastCGI stdout while reading response header from upstream, client:
>   123.234.123.234, server: test.mydomain.tld, request: "GET / HTTP/1.1",
>   upstream: "fastcgi://unix:/tmp/cgi.sock:", host: "test.mydomain.tld:8443"

Without /tmp/fcgi.sock being correctly available:

curl -i http://127.0.0.1:8008/x?k=v

returns "HTTP/1.1 502 Bad Gateway" and the nginx error log says what
nginx saw the problem to be -- "no such file" or "permission denied"
indicate that the socket is not listening correctly; "upstream prematurely
closed" suggests that the problem is on the fcgiwrap side -- check its
logs, or investigate further.

Perhaps /tmp/script is not executable by the fcgiwrap user, or does
not provide correct CGI output when run in this limited environment. Or
perhaps something else on your system prevents this file in /tmp from
being executed -- it's your system, only you know how it is configured
and where the logs are that report failures. (Perhaps you have to move
/tmp/fcgi.sock to somewhere else; perhaps you have to move /tmp/script
to somewhere else.)

So, turn on fcgiwrap, ensure that the declared socket is readable and
writeable by the nginx user, and ensure that the declared script is
executable.

(In this case, I just do "env -i /usr/local/bin/fcgiwrap -s
unix:/tmp/fcgi.sock"; but you do whatever your system wants in order to
achieve the same thing.)

Now:

curl -i http://127.0.0.1:8008/x?k=v

returns "HTTP/1.1 200 OK" with some useful content (in my case: 10 lines
of output). It works, hurray.

That content includes the HTTP_ variables that were the client request
headers. It does not include things like QUERY_STRING and the like, which
are common CGI variables. That is because nginx was not configured to send
them to fcgiwrap, so fcgiwrap did not expose them to /tmp/script. Maybe
your "real" CGI script requires that some of those variables are set,
and will fail if they are not.

So change the nginx config to include "the usually sensible parameters"
-- although only you know what is sensible in your particular case,
so you may want to edit this to taste. In the nginx.conf, add one line
so that you have

==
server {
  listen 8008;
  location / {
    fastcgi_pass unix:/tmp/fcgi.sock;
    fastcgi_param SCRIPT_FILENAME /tmp/script;
    include fastcgi_params;
  }
}
==

Now "curl -i http://127.0.0.1:8008/x?k=v" returns more output (in my
case: 27 lines) including things like DOCUMENT_ROOT and REQUEST_URI
and DOCUMENT_URI and all of the other things that you can see in the
fastcgi_params file.

> I use fcgiwrap 1.1.0 from 2013, which appears to be the latest available
> release according to https://github.com/gnosek/fcgiwrap .

> I don't know how to debug this further. Development of fcgiwrap seems to
> have ended years ago and the project page is no longer connected. I'd be
> grateful for more ideas how to solve this puzzle.

In this test case, I am using "fcgiwrap version 1.0.1" from Grzegorz
Nosek, because that one happens to be lying around on this system. It
does not need much in the way of active development, since it works,
and the interfaces it implements have not changed recently.

All the best,

	f
-- 
Francis Daly        francis at daoine.org


More information about the nginx mailing list