FastCGI PHP File not found only when FETCH is used - php

I'm doing a little project to learn Docker - NextJS + PHP backend with Nginx.
Now I got to a stage when I can say it works somehow. When reaching the API via browser adress bar, it gives me the error page - That's right. Nginx not complaining.
BUT when I try to FETCH it, it gives me file not found from the PHP-FPM container and the Nginx complains FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream
The site config is this
upstream next_app {
# NextJS running app port
server nextapp:3000;
}
upstream php_fpm {
# PHP FPM server URI and port
server phpapp:9000;
}
server {
listen 80 default_server;
server_name _;
server_tokens off;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
location ~ ^/api {
root /var/www;
try_files /www/index.php =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php_fpm;
}
# proxy pass for NodeJS app
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
location / {
proxy_pass http://next_app;
}
}
The basic idea was to use PHP api on any request with query starting with /api.
I have a little suspicion on the Next app serving me different Fetch, but I didn't found anything to help it
Any idea how to make it work? Thanks

So, solved now, thanks to #ad7six comments I realized the problem is more accurately described by
"How the hell can I print out a Nginx variable when everything around fails?"
There's two ways - use return and it comes with the response body or
add_header with the always attribute.
add_header X-cgiscript 42 always;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/www/index.php;
fastcgi_pass php_fpm;
So now the FPM only executes the index.php of the whole app.
Thanks

Related

Getting a user's IP address in Laravel through Docker containers and Nginx webserver

Good day,
My problem is that I am ONLY getting my Docker network IP address when using Laravel's $request->ip() or $request->getClientIp () or $request->server->get('REMOTE_ADDR') or $_SERVER['REMOTE_ADDR'] which essentially all do the same I assume.
So far the problem occurs on my local test pc (without Nginx Reverse Proxy) as well as on my live servers
I hope I've supplied enough relevant info below, if not please let me know :) Thanks in advance!
So my setup is the following (simplified), both Nginx configs are pasted below as well:
Server -> Nginx Reverse Proxy -> Docker Containers & Network (db, php, nginx, redis) -> Nginx Webserver <-> PHP-FPM
Some results related to _SERVER:
[SERVER_ADDR] => 172.20.0.3 (nginx webserver)
[REMOTE_PORT] => 55378
[REMOTE_ADDR] => 172.20.0.1 (docker network / gateway)
[SERVER_SOFTWARE] => nginx/1.19.6
I have read multiple forums and solutions for hours and hours while trying and changing configs etc. but none of it seems to be working.
What I have tried:
Get User IP address in laravel with similar method to HTTP_X_FORWARDED_FOR
https://serverfault.com/questions/618456/getting-the-client-ip-when-passing-through-a-reverse-proxy
So I have also tried to set my "Trusted Proxy" in Laravel's /Http/Middleware/TrustProxies.php by allowing all: protected $proxies = '*';
I also don't need these additional Laravel packages as of >5.5 I believe this is a built in feature of Laravel (TrustProxies).
Please find below my Nginx webserver config:
server {
listen 80;
server_name domain.com;
return 301 https://www.example.com;
}
server {
listen 80 default_server;
index index.html index.htm index.php;
server_name www.example.com;
root /var/www/public;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm:9013;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/example.com/public/$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
# Support Clean (aka Search Engine Friendly) URLs & enable Gzip compression:
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
try_files $uri $uri/ /index.php?$query_string;
gzip_static on;
}
# Override the load balancer IP with real IP.
fastcgi_param REMOTE_ADDR $http_x_real_ip;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
}
And my server's Nginx Reverse Proxy config (IF relevant):
server {
listen 80;
listen [::]:80;
server_name example.com.au www.example.com.au;
location / {
proxy_pass http://localhost:8013;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
}
Ok figured it out! :D
I was using Cloudflare as a proxy, my DNS is with CloudFlare and I have enabled the "Proxy through CloudFlare" option due to speed / caching improvements.
This resulted in CloudFlare "injecting" a HTTP_CF_CONNECTING_IP into my header.
To retrieve this header value I have used $_SERVER['HTTP_CF_CONNECTING_IP'] in my code.
Another solution was to use the HTTP_X_FORWARDED_FOR value from the header by using _SERVER['HTTP_X_FORWARDED_FOR'], this I believe would, in my situation only work after doing the following (posted in my question):
TrustProxies.php
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
I hope this helps others as well :D

Detect URL Query Parameter From Request in Nginx Config On Virtual Host

I have few virtual hosts (sites) running on this single server.
Right now, on this root virtualhost i have a forum (running on Docker) but served by Nginx mysite.com and I have its AMP pages being served on /amp route which is
mysite.com/amp. These AMP pages are basically 1 single index.php file & they all are handled by this 1 file. These are served by PHP using Nginx.
What I want is, when a user hits any of these requests matching below patterns: (like if ANY URL on this domain ending with ?amp=1
mysite.com?amp=1
mysite.com/t/my-topic/121?amp=1
mysite.com/c/CategoryCaseInsensitive/13?amp=1
mysite.com/u/john?amp=1
mysite.com/u/john/summary?amp=1
THEN
I want to redirect this request and send it to my AMP page (which is running on PHP file and will be then served/handled by index.php which is present in /var/www/amp ) . Right now the PHP code is being served on /amp but i want to serve it on mysite.com?amp=1 so any URL preceding ?amp=1
I have tried this code but its not seem to working for all cases:
#if ($arg_amp) {
# return 302 /amp$request_uri;
#}
Below is my current NGINX config file for this virtual host:
#Vhost Config Server, serving Ruby on Rails App on Docker on domain root
server {
listen 443 ssl http2;
ssl on;
ssl_certificate /var/www/cert/mysite.pem;
ssl_certificate_key /var/www/cert/mysite.key;
server_name mysite.com www.mysite.com;
location / {
proxy_ssl_server_name on;
proxy_pass http://localhost:PORT;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
# Socket.IO Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#if ($arg_amp) {
# return 302 /amp$request_uri;
#}
}
#Serving PHP code on /AMP route
location #amp {
rewrite ^/amp(.*) /amp/index.php?q=$1;
}
#will match any prefix for amp, amping, or amp/anything/any
location /amp {
index index.php;
try_files $uri $uri/ #amp;
alias /var/www/amp;
#PHP config for Nginx
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
}
#/amp route ends
}
Is this not possible in Nginx?
I think it could be done more simple way. If your index.php is the only PHP file needs to be served, you don't need this whole location ~ \.php$ { ... } thing.
server {
...
location / {
if ($arg_amp) {
return 302 /amp$request_uri;
}
...
}
location /amp {
# if you really need an original URI as a query argument
rewrite ^/amp(.*) /amp/index.php?q=$1 break;
include fastcgi_params;
# hardcode script path
fastcgi_param SCRIPT_FILENAME /var/www/amp/index.php;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
}
Or you can skip that rewrite rule at all, just get an original request URI from $_SERVER['REQUEST_URI'] and strip /amp prefix from it within your PHP code:
location /amp {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/amp/index.php;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
You can even process those requests with your script without 302 redirects:
server {
...
location / {
if ($arg_amp) {
rewrite ^ /amp$request_uri last;
}
...
}
location /amp {
internal;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/amp/index.php;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
}
An original URI would be accessible from $_SERVER['REQUEST_URI'].

Laravel-Websockets, Nginx, Docker, Apache Mesos & more in this stack - 404 error

When we setup laravel-websockets locally in docker, everything worked wonderfully; however, when it all got pushed to prod, problems arose. Consequently, we have to split up our main site into one server and the websocket server into a different server. All of these sites are on a corporate internal network and do not use SSL.
So, the main application is Laravel 6 running on Centos, Apache 2.4, and PHP 7.3. It can connect to the websocket server but anytime laravel broadcasts an event we get a 404 error for this endpoint:
apps/XXXXXX/events?auth_key=XXXXXXX&auth_signature=XXXXXXX&auth_timestamp=XXXX&auth_version=1.0&body_md5=XXXXX
The websocket server is Centos, PHP 7.3, Nginx 1.8 setup as a reverse proxy using spaties / beyondcodes example conf file.
map $http_upgrade $type {
default "web";
websocket "ws";
}
server {
listen 80;
server_name XXXXXXXXX;
root /opt/rh/rh-nginx18/root/usr/share/nginx/www/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.html index.htm index.php;
charset utf-8;
location / {
try_files /nonexistent #$type;
}
location #web {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 500 502 503 504 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location #ws {
proxy_pass http://127.0.0.1:6001;
proxy_set_header Host $host;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
# Allow the use of websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
My whole team is new to nginx so we figure there is probably an issue there or something else that we have setup incorrectly. We know our events and broadcasting within the main laravel app is correct, because it worked when it was all running on a single Apache cluster. Our issue was, that corporate would not open a port in the firewall other than 80 and 443. We are opting to split traffic across 80 with nginx as per beyondcode's documentation.
Any thoughts on this? We are at a loss. Thank you all!
Edit:
I should add we are using Laravel Echo with Pusher.JS for the front end:
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
wsHost: 'XXXXXXXXXX',
wsPort: 80,
disableStats: true
});

Proper handling of request_uri in double nginx reverse proxy?

So, essentially I am running Joomla in a Docker php7-fpm container, then I have an nginx container where a joomla.conf file is defined as follows:
#https://docs.joomla.org/nginx
server {
listen 8081;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
server_name php-docker.local;
root /usr/src/joomla;
index index.php index.html index.htm default.html default.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
# deny running scripts inside writable directories
location ~* /(images|cache|media|logs|tmp)/.*\.(php|pl|py|jsp|asp|sh|cgi)$ {
return 403;
error_page 403 /403_error.html;
}
location ~ \.php$ {
fastcgi_pass joomla:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
#include /etc/nginx/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
And this works as expected... going to http://:8081 loads everything correctly.
Now, 8081 is just temporarily exposed in the nginx container, what I essentially want to do is setup a reverse proxy such that http:///joomla will be the final endpoint.
For this, I am struggling with the following conf file:
server{
listen 80;
server_name _;
location /joomla/ {
proxy_pass http://localhost:8081/;
proxy_set_header Referer $http_referer;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
}
}
What happens is that the HTML is served correctly, however, none of the assets are. This is because the URLs in Joomla are generated by a JURI class, which seems to rely on $request_uri, which by when it appears to arrive to Joomla is already lost.
https://github.com/joomla/joomla-cms/blob/6ab2a6e9010e7e04c260b9eba17dc76e866dd3e6/libraries/joomla/uri/uri.php#L87
So every link or reference to a file, script, or css renders like this:
http://localhost/login
http://localhost/images/headers/maple.jpg
Instead of:
http://localhost/joomla/login
http://localhost/joomla/images/headers/maple.jpg
However, when I access the second set of URL, I can access the link/asset without a problem... but of course once again, no images, templates, js or links being rendered correctly.
I prefer not to touch joomla.conf unless something is wrong, as for site.conf I would only like to translate URI segments to map requests to other applications, e.g.:
/joomla -> localhost:8081
/phpbb -> localhost:8082
/someapp -> localhost:8083
The cleanest solution would be to modify your upstreams to provide unique paths for all the resources.
It is generally a much easier task to remove parts of a URL from the upstream (with a unique prefix) than to add extra parts to a non-unique one. This is because you can always catch the longer URL and know exactly what it refers to, subsequently returning a 301 or 302 redirect to a shorter and more concise version.
On the other hand, faced with a short request URL, like /, it would be difficult to know for sure which app it may refer to (unless you look into the $http_referer variable, too, and then conditionally issue the redirect based on where the URL request comes from), or unless you implement some sort of spaghetti rules to detect which individual URLs refer to which applications (if you go this route, the map directive may come in handy).
Additionally, consider that, security-wise and when cookies get involved, it is not the best practice to run multiple independent applications on a single domain — a compromise in one application can easily lead to security violations in all the other ones.
As Rinos said, you need to configurate the $live_site var in configuration.php, as this:
public $live_site = '/joomla/';
I've made a complete example in Github that works well after editing that file, and it uses your configs.
 Becomes:
You could try use sub_filter how it mentioned in this answer.
So in your case your nginx config should looks like this one:
server{
listen 80;
server_name _;
location /joomla/ {
proxy_pass http://localhost:8081/;
proxy_set_header Referer $http_referer;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
sub_filter "http://your_server/" "http://your_server/joomla/";
sub_filter_once off;
}
}
I know this is old but here's my answer. You shouldn't change the configuration.php file, basically because you should keep your services (web and proxy) completely separated (A problem generated by a service implementation should be solved in that service), that will make your service architecture more flexible.
You have to filter the assets urls and some Location headers changing the following proxy settings.
server {
#... other settings
location /joomla/ {
# Main redirect
proxy_pass http://joomla_service/;
# Set the Host Header
proxy_set_header Host $host;
# When Joomla redirects a Location Header
# For example when you log in, Joomla retrieves you a response with a Location Header = localhost
# we don't want this.
proxy_redirect http://localhost/ http://localhost/joomla/;
proxy_redirect / /joomla/;
# Some content rewriting
sub_filter_types text/javascript application/javascript;
# Html redirection
sub_filter '="/' '="/joomla/';
sub_filter '="http://localhost/' '="/joomla/';
sub_filter '\'http://localhost/administrator/' '\'/joomla/administrator/';
sub_filter 'url(\'/media' 'url(\'/joomla/media';
# Some json data
sub_filter '"root":""' '"root":"\/joomla"';
sub_filter '"uri":"\/index.php' '"uri":"\/joomla\/index.php';
sub_filter '"jdragdrop":"\/media' '"jdragdrop":"\/joomla\/media';
sub_filter '"plg_quickicon_privacycheck_url":"http:\/\/localhost' '"plg_quickicon_privacycheck_url":"\/joomla';
sub_filter '"plg_quickicon_privacycheck_ajax_url":"http:\/\/localhost' '"plg_quickicon_privacycheck_ajax_url":"\/joomla';
sub_filter '"content_css":"\/templates' '"content_css":"\/joomla\/templates';
sub_filter_once off;
}
}
For some reason Joomla is not doing a good support if we talk about implement a reverse proxy. I created an example with more details, you can see it here.

Running Node.js app and PHP in same machine

I am really confused whether it is possible ? Please help me out, I have Node.js application, say node_app, running on X port, and PHP application, say my_app, running in Apache's default 80 port. I have one domain name only. What my problem is, if user hit domain.com/my_app, it should run the PHP application in 80's port. If the user hits domain.com/node_app, it should run the node application in X port. And one more important constraint is the end-user should not see any port number in URL bar.
You can install Node.JS and PHP in the same host, using Nginx as proxy per exemple.
Per exemple, with Nginx, you could create two virtualhosts :
Default virtual host using PHP (FPM or not) who points to exemple.tld
Second virtual host to another node.exemple.tld
First VH is gonna be like this (with PHP-FPM) :
server {
listen 80; ## listen ipv4 port 80
root /www;
index index.php index.html index.htm;
# Make site accessible from exemple.tld
server_name exemple.tld;
location / {
try_files $uri $uri/ /index.php;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 and using HHVM or PHP
#
location ~ \.(hh|php)$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_keep_conn on;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}
Second VH with NodeJS :
server {
listen 80;
server_name node.exemple.tld;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
access_log off;
# Assuming that NodeJS listen to port 8888, change if it is listening to another port!
proxy_pass http://127.0.0.1:8888/;
proxy_redirect off;
# Socket.IO Support if needed uncomment
#proxy_http_version 1.1;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection "upgrade";
}
# IF YOU NEED TO PROXY A SOCKET ON A SPECIFIC DIRECTORY
location /socket/ {
# Assuming that the socket is listening the port 9090
proxy_pass http://127.0.0.1:9090;
}
}
As you can see, it's possible, and pretty easy to do!

Categories