Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I'm moving a legacy Zend Framework application over to Nginx + php-fpm. Here are my relevant configs:

server {
  listen 80;

  server_name *.sandbox.journalexperts.com;
  port_in_redirect off;
  server_tokens off;
  autoindex off;

  include /etc/nginx/conf.d/assets.conf; # this file contains some info about not access logging robots.txt, favicon.ico, etc.

  client_max_body_size 15m;
  client_body_buffer_size 128k;

  root /wwwroot/vhosts/$host;
  index index.html index.php;
  access_log off;

  # deliver a static 404
  error_page 404 /404.html;
  location /404.html {
      internal;
      root html;
      allow all;
  }

  # Deliver 404 instead of 403 "Forbidden"
  error_page 403 = 404;

  # Deny access to hidden files
  location ~ /\. {
      deny all;
      access_log off;
      log_not_found off;
  }

  location / {
      try_files $uri $uri/ /index.php$is_args$args;
  }

  location ~ \.php$ {
      include /etc/nginx/conf/fastcgi.conf;

      fastcgi_intercept_errors on;
      fastcgi_ignore_client_abort off;
      fastcgi_connect_timeout 60;
      fastcgi_send_timeout 180;
      fastcgi_read_timeout 180;
      fastcgi_buffer_size 128k;
      fastcgi_buffers 4 256k;
      fastcgi_busy_buffers_size 256k;
      fastcgi_temp_file_write_size 256k;

      fastcgi_pass 127.0.0.1:9000;
      fastcgi_index index.php;
  }
}

This is my fastcgi.conf file:

fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $host;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

# http://wiki.nginx.org/HttpFastcgiModule#fastcgi_split_path_info
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

fastcgi_param SCRIPT_URL $script_url; # This is a mapped variable.
fastcgi_param SCRIPT_URI $scheme://$http_host$script_url; # Uses above mapped variable.

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  PHP_SELF           $uri;
fastcgi_param  HTTPS              $https if_not_empty;

I need to mimic the behavior of SCRIPT_URL and SCRIPT_URI as they are populated in Apache. I found this thread where the poster used map to fill these.

map $uri $script_url {
    ~^(?<script_filename>.+\.(php|html))(?<path_info>.+)$ $path_info;
    ~^(?<script_filename>.+\.(php|html))$ $script_filename;
}

This, unfortunately, does not work for me and shows /index.php as the SCRIPT_URI. I suspect that this is because of my try_files block where I send the request over to /index.php. This is supported by the Nginx documentation:

$uri
This variable is the current request URI, without any arguments (see $args for those). This variable will reflect any modifications done so far by internal redirects or the index module. Note this may be different from $request_uri, as $request_uri is what was originally sent by the browser before any such modifications. Does not include the protocol or host name. Example: /foo/bar.html

Okay, let's try rewriting the map using $request_uri.

map $request_uri $script_url {
    default $request_uri;
    ~^(?<script_filename>.+\.(php|html))(?<path_info>.+)$ $path_info;
    ~^(?<script_filename>.+\.(php|html))$ $script_filename;
}

This is slightly better but still does not achieve the desired result. Here's what I get in Apache:

$_SERVER['SCRIPT_URL']: /tos/show.php/article/0000
$_SERVER['SCRIPT_URI']: http://example.com/tos/show.php/article/0000

This is what I get in Nginx:

$_SERVER['SCRIPT_URL']: /tos/show.php/article/0000?hello=world
$_SERVER['SCRIPT_URI']: http://example.com/tos/show.php/article/0000?hello=world

I don't know how to go about this.

share|improve this question
    
Fixing the PHP code wasn't an option? –  Michael Hampton Mar 14 '13 at 22:44
    
Sadly, no. That was my preferred solution, but it turns out that a few different legacy applications use these two variables, as do some third party libraries we use, and who knows what else. –  Vic Mar 15 '13 at 1:17

2 Answers 2

Looks like you just need to strip the query parameters off:

map $request_uri $script_url {
    default $request_uri;
    ~^(?<script_filename>.+\.(php|html))(?<path_info>.+)(\?.*)?$ $path_info;
    ~^(?<script_filename>.+\.(php|html))(\?.*)?$ $script_filename;
}
share|improve this answer
    
Thanks for the suggestion. Unfortunately, that didn't change anything for me. To verify that the new pattern was matched, I changed the default to '/whatever' and that's what ended up getting returned. –  Vic Mar 14 '13 at 18:03
up vote 0 down vote accepted

This is what I ended up doing, which works.

nginx.conf, in the http{} block:

map $request_uri $my_script_url {
    default $request_uri;
    ~^(?<script_filename>.+\.(php))(.*)?$ $script_filename; #/test.php or /test.php?hello=world
    ~^(?<script_filename>.*)(\?.*)$ $script_filename; #/tos?hello=world
    ~^(?<script_filename>.*)(\?.*)?$ $script_filename; #/tos or /tos/hello/world or /tos/hello/world?omg=what
}

Things I learned:

  • the regular expressions are matched top down and processing stops when the first match is found.
  • the nginx documentation could have done a lot more for me than it actually did.

Inside the server{} block:

location / {
    try_files $uri $uri/ /index.php$is_args$args;
}

location ~* \.php(.*)?$ {
    include /etc/nginx/conf/fastcgi.conf;
    [...]
}

fastcgi.conf:

fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $host;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

# http://wiki.nginx.org/HttpFastcgiModule#fastcgi_split_path_info
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

fastcgi_param SCRIPT_URL $my_script_url;
fastcgi_param SCRIPT_URI $scheme://$http_host$my_script_url;

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  PHP_SELF           $uri;
fastcgi_param  HTTPS              $https if_not_empty;

try_files $uri =404;

php.ini:

; cgi.fix_pathinfo = 1
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.