7

I have a very simple PHP site:

.  
├── about.php  
├── index.php  
├── project  
│   ├── project_one.php  
│   └── project_two.php  
└── projects.php  

And the following nginx config (only relevant parts shown):

location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass unix:/path/to/php.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_intercept_errors on;
}

location / {
    index index.php;
    try_files $uri $uri/ $uri.php =404;
}

Hitting the / works as expected. Hitting any of the http://my.site.com/{projects | about | project/*} URLs should use try_files to hit the $uri.php file, and pass it to PHP. But instead, the browser just downloads the PHP file itself.

I can get it to work by adding individual location directives for the above locations, like so:

location /projects {
    try_files $uri $uri/ /$uri.php;
}
location /about {
    try_files $uri $uri/ /$uri.php;
}
location /project {
    try_files $uri $uri/ $uri.php;
}

But this is clearly not the way to do this.

What am I doing wrong???

TL-Eugene
  • 304
  • 1
  • 2
  • 13

2 Answers2

16

Per nginx documentation for try_files

Checks the existence of files in the specified order and uses the first found file for request processing; the processing is performed in the current context

so nginx find PHP file and process it in context of location / therefor just serve it as static file. Only last parameter is different, it's not checked but nginx make internal redirect (if it's uri) or return error code (if it's =code). So you need to remove =404 from try_files to have internal redirect. And add try_files to location ~ \.php to make sure that file exists.

location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass unix:/path/to/php.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_intercept_errors on;
}

location / {
    index index.php;
    try_files $uri $uri/ $uri.php;
}
Alexey Ten
  • 13,794
  • 6
  • 44
  • 54
  • 1
    This is totally the right solution. Removing `=404` from `/` did the trick. I still need a separate location for /project, to catch a corner case where it would cause a redirect loop. But your solution simple and universal. Thanks. – TL-Eugene May 29 '14 at 21:26
  • For the curious, an example to check several php files (`$uri.php $uri/index.php, ...`) can be found [here](https://stackoverflow.com/questions/64742189/nginx-try-files-with-name-as-the-last-word-in-the-uri/64742548#64742548). – Ivan Shatsky Nov 16 '20 at 12:54
1

Try this:

location / {

    try_files $uri $uri/ /index.php;
    index  index.php;

    ## output: http://my.site.com/about
    rewrite ^/about$ /about.php last;

    ## output: http://my.site.com/projects
    rewrite ^/projects$ /projects.php last;

    ## output: http://my.site.com/projectOne
    rewrite ^/projectOne$ /project/project_one.php last;

    ## output: http://my.site.com/projectTwo
    rewrite ^/projectTwo$ /project/project_two.php last;
}

UPDATE:

location / {

    try_files $uri $uri/ /index.php;
    index  index.php;

    rewrite ^(.*)$ /$1.php; # WITHOUT PHP EXTENSION   
}
Black Sheep
  • 6,604
  • 7
  • 30
  • 51
  • This works, I optimized a bit: `rewrite ^/project/(.*)$ /project/$1.php last;`, but I feel like it's still partway between creating separate locations like I did before, and the *correct* solution. There *has* to be a way to make this happen without creating a rewrite for each PHP script that is added to the site.... Right?? or am i being overly optimistic? – TL-Eugene May 28 '14 at 17:37
  • I do not know if I quite understood you... What you want is to hide only the php extension? Show my updated answer... – Black Sheep May 28 '14 at 21:53
  • The updated version is not correct. It will rewrite the URL / to "/.php" and that results in 403 error (or 404 - depends on the configuration). `rewrite ^(.+)/$ /$1.php;` works better. – Johnny Vietnam Nov 08 '19 at 09:39