How to remove a path segment from NGiNX fastcgi_script_name? - php

This question could also be: "How to modify an NGiNX variable with a RegEx?",
or: "Can RegEx backreference have gaps?"
Either of those would resolve the problem I'm having. Perhaps a really simple solution exists, and after digging the web for few hours, it's time to ask for help.
Here's the scenario:
There's a part of the request URI that is going to be present always (for a sort of a gimmicky domain name + URI combination :-). I enforce the presence of that always-present URI path component, which follows immediately after the domain name, like so:
http://somedomain.com/basepart/rest/of/the/path?q=123
In the above example the "/basepart" represents the always-present URI component.
So far so good. The problem arises when I want the base file path to be /var/www/somedomain.com/htdocs/ without the basepart, and php5_fpm proxy is used. I obviously set:
location /basepart {
alias /var/www/somedomain.com/htdocs;
try_files $uri $uri/ /index.php?$args;
}
But since the dynamic files are in PHP, I need to either use fastcgi_split_path_info or $request_uri to build/pass the SCRIPT_FILENAME to php5_fpm. How do I do that? How do I remove the /basepart from $fastcgi_script_name, or from $request_uri, as otherwise PHP will look for the file in /var/www/somedomain.com/htdocs/basepart?
I've considered named backreferences, or "collecting" or "fragmented" backreferences (which I don't think exist in regex) so that I could capture the segment in $fastcgi_script_name before and after the basepart when fastcgi_split_path_info assignment happens, but haven't got them to work. Dayo writes earlier at SO: »Nginx is a webserver and not a scripting application.», and suggests use of Lua for more complex scripting. But I have a feeling I may be overlooking some really simple, facepalm-worthy solution :-].
Any thoughts, anyone?

If someone else stumbles on this question, after some brainstorming i came up with stupidly obvious solution:
root /app/frontend/web/;
location /api/ {
alias /app/backend/web/;
index index.php index.html;
# Double /api/ because "that's how nginx alias and try_files works together"
try_files $uri /api//api/index.php$is_args$args;
location ~ ^/api(/.*\.php(?:\?.*)?)$ {
try_files $uri /api//api/index.php$is_args$args;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$1;
...
}
}
I.e. using regex capturing group.
With this rules requests will be routed following way:
/index.html -> /app/frontend/web/index.html
/test/test.php -> /app/frontend/web/test/test.php as plaintext
/api/<somepath> -> /app/backend/web/<somepath> (proxied to FPM if .php) if it exists, otherwise /app/backend/web/index.php

The alias directive is fine for static websites but not so useful when PHP is involved. My preferred solution is to internally rewrite the URI without the /basepart and then use root rather than alias.
The problem is that many PHP scripts use $request_uri in order to process the request, which is frozen with the /basepart intact. However, we can specify any value we choose for REQUEST_URI and construct a more appropriate value from $uri or captures. In the example below, I preserve the value of $uri after the first rewrite so that it can be used to pass our modified request URI to the PHP script.
root /var/www/somedomain.com/htdocs;
location ^~ /basepart {
rewrite ^/basepart/(.*)$ /$1 last;
rewrite ^ / last;
}
location / {
internal;
try_files $uri #index;
}
location #index {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param REQUEST_URI $uri;
...
}
location ~ \.php$ {
internal;
try_files $uri #index;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param REQUEST_URI $uri;
...
}
The fastcgi code block is duplicated across two locations. If it becomes unwieldy, the common code can be placed into a separate include file.
Locations are made private by using the internal directive keeping the /basepart mandatory for external access.

Related

nginx php-fpm with dynamic url segments not being parsed by php-fpm

so this one is a weird one! I have these nginx stanzas. I used an online tool to convert .htaccess rules into nginx capable ones.
location / {
rewrite ^/(([^/]+/)*)login/$ /login.php?path=$1&$query_string break;
rewrite ^/(([^/]+/)*)comment/$ /comment.php?path=$1&$query_string break;
rewrite "^/([^/]+/){4}$" /asset.php break;
rewrite ^/(\d\w+)/$ /public.php?ticket=$1 break;
rewrite "^/(\d\w+)/([^/]+/){2}?$" /public-asset.php?ticket=$1 break;
if (!-e $request_filename){
rewrite ^/(([^/]+/)*) /index.php?path=$1&$query_string break;
}
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/var/run/php/vh1-fpm.sock;
include fastcgi_params;
}
going to https://example.test/ ok
going to https://example.test/login/ all heck break loose and PHP code is flooded onto the screen.
The regex routing works because the redirects take place. but once the redirect it done, it's like the masked .php file is not caught. All this is fixed if I encapsulate the PHP checks within the / location block.
You cannot use rewrite ... break when the rewritten URI is to be processed in a different location block.
Use: rewrite...last
Also, there is no need to tack &$query_string on the end of the rewritten URI, as rewrite will do it anyway unless the rewritten URI ends with a ?.
See this document for details.

How to configure nginx to map only the first path in URL to a specific directory for php app?

I'm working on a php web app and want the URLs to be mapped to a folder on the server like this:
https://app.com/clientOne/... --> /client/...
https://app.com/clientTwo/... --> /client/...
etc
And all folders following clientOne and clientTwo in the URL exist on my server under client E.g. clientOne/admin/ gets mapped to client/admin/, and clientTwo/app/ gets mapped to client/app/. I want all clients to share the same application, and the application will determine the client from the URL. (I don't want to deal with subdomains right now.)
My nginx config works only when directories are in the URL. When I reference specific php files, I get a 404:
https://app.com/clientOne/admin/ (works)
https://app.com/clientOne/admin/index.php (returns 404)
I've searched and haven't found anyone with this particular issue. I think I know what needs to be done, but everything I have tried has not worked. Here is my standard nginx config:
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_pass unix:/var/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
and this:
location ~ ^/(clientOne|clientTwo)/(.*)$ {
try_files $uri $uri/ /client/$2?$query_string;
}
I'm thinking I need to update the php location block to address my issue and then I won't need the second location block, but everything I have tried has not worked. I tried changing the try_files in the php location block to this, but it didn't work:
try_files $uri /client/ =404;
Does anyone know how to do this? Thanks

NGINX location fallback to Symfony

I'm working on a legacy API project written in pure PHP and now trying to move it to Symfony 4 and need to make some changes to the NGINX configuration.
I am required to keep both the old and the new endpoints working at the same time, so my plan is to setup NGINX in a way that it tries to serve first the old endpoint and if it doesn't exist (since I plan to remove them as I migrate to Symfony) it redirects to Symfony's front controller.
The current directory structure is this:
So far my NGINX conf is like so:
server {
root /project/www;
index index.php;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm:9000;
include fastcgi_params;
fastcgi_param HTTPS on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ / {
try_files $uri $uri/ /index.php$is_args$args;
}
}
It works fine, if I request /api/client_status it uses the index.php under the client_status directory and if I remove that folder it uses Symfony's front controller.
The thing is that other applications are using these enpoints sometimes with index.php appended, e.g /api/client_status/index.php, and in these cases I get a 404 instead of being redirected to Symfony's front controller.
I tried rewriting at the top of the server block with rewrite ^/api/(.*)/index.php /api/$1; but it didn't help. Any input or even a suggestion of another approach is very welcome.
The URIs with index.php appended, will be processed by the location ~ \.php$. You need to add a try_files statement to this block to avoid the 404 responses, and instead send the requests to Symfony's front controller.
For example:
location ~ \.php$ {
try_files $uri /index.php$is_args$args;
...
}

Pass entire URL to $_GET variable in PHP built-in server

I'm running a PHP built-in server with
php -S 127.0.0.1:80 index.php
I want to pass the entire URI string to a field called "url" in the $_GET array. When I enter http://localhost/thisIsAURLString, I want var_dump($_GET); to return array(1) { ["url"]=> string(16) "thisIsAURLString" }
Is there some way to do this with the PHP built in server?
The web application is usually run in a production environment with nginx, and with a configuration file as shown below. This configuration passes the URL to a field "url" in the $_GET variable, but I want to do something similar with the PHP built-in server.
server {
listen 5001 default_server;
listen [::]:5001 default_server ipv6only=on;
root [myRoot];
index index.php index.html index.htm;
server_name [myServerName];
location /uploads {
try_files $uri $uri/ =404;
}
location /assets {
try_files $uri $uri/ =404;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
rewrite ^/(.*)$ /index.php?url=$1 last;
}
location ~ .php$ {
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_index index.php;
fastcgi_pass unix:/var/run/php/php7.0-fpm-01.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
}
EDIT (some context) :
The context is that I'm a TA with many students. The web application in question is currently in a production environment with nginx and runs smoothly, but all of my ~100 students need to download and deploy the very same web application locally on their own computers. I can't alter the PHP code. The deployment should be as simple and smooth as possible, and if they can do this with some easily reproducible php command, that would be ideal.
You could bootstrap your application with this script. Save this snippet to a file, and set it as your entry point in whatever web server software you're using. It will produce the results you're asking for.
<?php
$root=__dir__;
$uri=parse_url($_SERVER['REQUEST_URI'])['path'];
$page=trim($uri,'/');
if (file_exists("$root/$page") && is_file("$root/$page")) {
return false; // serve the requested resource as-is.
exit;
}
$_GET['url']=$page;
require_once 'index.php';
?>
I'm not sure what you're asking but let me start with:
What "field" are you talking about?
Are you trying to print the url where?
What do you mean by "PHP built-in server"?
$_GET is a superglobal variable, array type, that's populated by PHP (a server-side scripting language). All you gotta do is call it (e.g. $_GET['link'] whereas link could be anything you'd want) or something similar (please check http://php.net/manual/en/reserved.variables.get.php). You can use it in any php file.
You may want to look at the global $_SERVER array. This contains the HTTP_HOST, QUERY_STRING, REQUEST_SCHEME and REQUEST_URI array keys. These can be used to assemble a full url. Try a var_dump($_SERVER); to see all the key => values.
Is there a particular reason you need to use the $_GET global array?
Hope this helps.

Clean URLs and php extension on nginx

I've looked at dozens of other questions and references on the web - and by all my calculations, my setup should work, but it doesn't.
I have nginx installation with php-fpm. If I try to access a .php file, it runs correctly and I get the correct results. I got this in my config file:
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php5-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Now, I want to setup my web app so that /somedir/file automatically executes /somdir/file.php while still displaying /somdir/file in the browser's address bar. So I modified my config to contain the following:
location / {
try_files $uri $uri/ $uri.php?query_string
}
This kind of works, that is, the server does access the .php file. Yet, instead of executing it using the existing location ~ \.php$ block above, it simply spits the php source code as the download into the browser. If I append the .php manually to the requested URL, then the php is executed.
It feels as if once the server matches try_files to $uri.php, it then does not do another pass at locations to see that what it needs to do with the php files. I tried putting the php block above and below the location /, but it makes no difference.
How can I get the php to be executed?
You want file.php to be treated as an index file, so that domain.com/dir/file.php works on domain.com/dir/ ?
Why not just rename it to index.php?
You can do this by adding this param on your location block:
index index.html file.php index.php;
If not, you might want to look into writing a rewrite rule for nginx to map domain.com/dir/ to domain.com/dir/file.php (but you have to do it for each dir that you need it to work)

Categories