nginx + fcgiwrap: how come order of fastcgi_param matters?
I'm running Debian 6.0.3
(squeeze
), nginx-0.7.67
, fcgiwrap-1.0-1+squeeze1
. Here is the test script:
#!/usr/bin/perl
use 5.010;
use warnings;
use strict;
use Data::Dumper;
print "Content-Type: text/html\n\n";
say Dumper {map {$_ => $ENV{$_}} 'SCRIPT_NAME', 'DOCUMENT_ROOT', 'WHATEVER'};
say "$<, $>, $(, $)";
And here's the nginx
configuration:
server {
server_name domain.com;
root /home/yuri/6;
access_log /var/log/nginx/domain.com-access.log;
error_log /var/log/nginx/domain.com-error.log;
location /cgi-bin/ {
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param DOCUMENT_ROOT /home/yuri/7/;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param WHATEVER 1;
fastcgi_param WHATEVER 2;
}
location /1.php {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_param PHP_ADMIN_VALUE cgi.fix_pathinfo=1;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME whatever;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Here's what I get in browser if going to url http://domain.com/cgi-bin/1.pl:
$VAR1 = { 'SCRIPT_NAME' => '/cgi-bin/1.pl', 'DOCUMENT_ROOT' => '/home/yuri/7/', 'WHATEVER' => '2' };
So it seems, that fcgiwrap
uses the first DOCUMENT_ROOT
for looking up for the script, but the script gets the last values of params. If you change the order of DOCUMENT_ROOT
directives, web server returns 403
. The question is... how come?
php
though works as expected: the second SCRIPT_FILENAME
overrides the first one.
Solution 1:
The fastcgi library just passes whatever parameters it was given into the environ
pointer. getenv()
as used by fcgiwrap
uses whatever environment variable came first (optimization?). It is likely that PHP FPM treats this environment just like the array data type, any latter key overwrites the first.
You should not rely on the order and make sure that there is only one key. I have not looked at the FastCGI specification, whether the correct behavior is documented or not.
As for why you get the 403, I guess that there is no /home/yuri/7/1.pl
file. (remember, the first parameter gets matched by fcgiwrap). Since it is not executable, fcgiwrap returns a 403.
Testing
The below patch prints the environment as given by FCGI_Accept()
:
diff --git a/fcgiwrap.c b/fcgiwrap.c
index e86ff9d..0dff2e6 100644
--- a/fcgiwrap.c
+++ b/fcgiwrap.c
@@ -470,6 +470,11 @@ static void inherit_environment(void)
char * const * p;
char *q;
+ for (p = environ; *p; p++)
+ fprintf(stderr, "ENV: %s\n", *p);
+
+ fprintf(stderr, "ENV[FOO] = %s\n", getenv("FOO"));
+
for (p = inherited_environ; *p; p++) {
q = strchr(*p, '=');
if (!q) {
nginx configuration used for the test:
events {
worker_connections 768;
}
pid pid;
error_log error_log;
http {
server {
access_log off;
listen 5555;
location / {
fastcgi_param FOO BAR;
fastcgi_param FOO BARRED;
fastcgi_pass unix:/tmp/sock;
}
}
}
Commands to start the servers (assuming nginx.conf
in the current directory):
$ nginx -p . -c nginx.conf
$ env -i ./fcgiwrap -p /dev/null -s unix:/tmp/sock
Now, run curl http://localhost:5555/
and the stderr will print:
ENV: FCGI_ROLE=RESPONDER
ENV: FOO=BAR
ENV: FOO=BARRED
ENV: HTTP_USER_AGENT=curl/7.30.0
ENV: HTTP_HOST=localhost:5555
ENV: HTTP_ACCEPT=*/*
ENV[FOO] = BAR
Cannot get script name, are DOCUMENT_ROOT and SCRIPT_NAME (or SCRIPT_FILENAME) set and is the script executable?
Aside, this is a development version. The current stable (1.1.0) does not contain the -p
parameter above. For the results it does not really matter, you could as well have dropped it and received an error that no SCRIPT_NAME
is given or something.