Nginx Proxy Pass Issue - Docker, React and Express - reactjs

I have a docker-compose set up that looks like this:
Containers:
Main nginx container gateway. The /todos/ route is proxy_passed to:
Another nginx container. This one proxy passes either to:
A React front end (for the todos app)
or
A node express api (which handles todo logic)
I'm trying to be able to call the todos api from the React app with process.env.PUBLIC_URL + "/api" in order to access the "/" route on my express api. However this isn't working, and I have to add another "/" to the React call to make it work - like this: process.env.PUBLIC_URL + /api/
Note: my React homepage is set to "/todos"
It's clearly a problem with how I'm doing the rewrites for the routes on Nginx, but I've tried every combination under the sun and can't get rid of the need for that trailing slash. Here are my Nginx configs:
The entry config:
upstream todosnginx {
server todosnginx;
}
server {
listen 80;
error_page 404 /404.html;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /todos {
rewrite /todos/(.*) /$1 break;
proxy_intercept_errors on;
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-Proto $scheme;
proxy_pass http://todosnginx/;
}
}
And the todos nginx:
upstream todoclient {
server todoclient:3000;
}
upstream todoserver {
server todoserver:9000;
}
server {
listen 80;
location / {
proxy_pass http://todoclient/;
}
location /sockjs-node {
proxy_pass http://todoclient;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://todoserver/;
}
}
I've tried doing:
rewrite /api(.*) /$1 break;
in the todosnginx conf,
and
location /api/
and I've tried adding trailing slashes or deleting trailing slashes and many other combinations. It would be great if you knew where I was going wrong here.
Many thanks, R

rewrite /api(.*) $1 break;
if it doesn't work, post the logs.

Related

How can I connect localhost of the host machine?

I was trying to host react as a frontend and rails as backend.
I host rails at http://localhost:3000.
You can see that it is working.
But when I call http://localhost:3000 from react, instead of going to server's localhost, it go to user's localhost.
How can I fix this? I am using Nginx as web server.
I just solved by doing reverse proxy in Nginx.
location / {
try_files $uri /index.html;
}
location /api {
proxy_pass http://localhost:3000;
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;
}
first location / will find static frontend files and /api will go to localhost:3000.
For example, in my case
this.url='https://yourdomain.com/api/' to access the backend.
Also don't forget all of your backend route to start with 'api/'.

React app doesnt launch from url location as set by nginx reverse proxy but does when port explicitly set

I'm trying to run a single react app on a separate port and using proxy pass from nginx to location '/app'
"http://localhost/app" -> React Application
The application is running under PM2 using its generated build with the command: 'pm2 serve build 3002'.
My thought was i do all this tested locally and then will port it to my own domain.
I've swapped 'localhost' for '127.0.0.1' just in case that had anything to do with it.
From online tutorials i've ended up with the following in my nginx "../sites-available/my_domain" file
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /var/www/my_domain/html;
index index.html index.htm index.nginx-debian.html;
try_files $uri $uri/ =404;
}
location /app {
proxy_pass http://localhost:3002;
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;
}
}
If i type "http://localhost:3002" into the url it works fine however "http://localhost/app" does not work and shows 404 error.
Side-note: "http://localhost" works fine and directs to the index.html stored at root.

NGINX proxy pass to React App not working

I want to run the following projects using NGINX under a single subdomain: http://localhost:3000 (Loopback API) and http://localhost:3006 (React Application)
Both applications are running under PM2. React App is running in production (using its generated build) with the command: 'pm2 serve build 3006'.
/etc/nginx/sites-available/default
server {
listen 80;
server_name subdomain.domain.com;
location / {
proxy_pass http://localhost:3006;
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;
try_files $uri /index.html;
}
location /api {
proxy_pass http://localhost:3000;
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;
}
Loopback project under location /api es working perfectly. The issue is with my React project under my / route. When I enter to subdomain.domain.com I just get a blank page. In the developer console I'm getting the following errors:
Using http://localhost:3006 to access my React App works perfectly fine with no console issues so I'm totally sure it is something related with nginx.
I have been investigating a lot about the incorrect MIME type being loaded, but my /etc/nginx/nginx.conf is already running with the following configuration:
http {
include /etc/nginx/mime.types;
}
I would really appreciate your help, thanks.
Are you using Create React App? By default, its build script assumes the app is going to be served in the root directory. To correctly serve React under a subdirectory, you need to specify homepage in your package.json file.
"homepage" : "http://example.com/your-subdirectory"
Additionally, you'll want to modify server/app.js to reflect this change.
app.use('/your-subdirectory/api', require('./api'));
Lastly, and most importantly, you'll want to set a basename for React Router as well.
const Routes = () => {
return (
<Router basename={'/your-subdirectory'}>
<div>
<Route exact path={`/`}component={App} />
</div>
</Router>
)
};
There is no need to use pm2 since they are static files and nginx is able to serve it by itself.
You just have to let nginx know where the files are.
server {
listen 80;
root /home/user/app/buld <--- LOCATION_OF_YOUR_BUILD
index index.html
server_name subdomain.domain.com;
location / {
try_files $uri /index.html
}
location /api {
proxy_pass http://localhost:3000;
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;
}

nginx loopback reverse proxy can't access custom paths

Im running this loopback app in my server and trying to access it through an nginx reverse proxy, but can't seem to get nginx configured correctly, I'm a total noob to nginx. Here's my config file /etc/nginx/sites-available/example.com
server {
listen 80;
server_name www.example.com;
server_name example.com;
server_name _;
root /apps/example/client;
location ~ ^/.well-known {
allow all;
}
location / {
return 301 https://example.com;
}
}
server {
server_name example.com;
server_name www.example.com;
#listen 80;
# SSL configuration
listen 443 ssl ;
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
root /apps/example/client;
# Check Alive
location ~ ^/.well-known {
allow all;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_bind 127.0.0.1;
proxy_pass http://127.0.0.1:3000/;
}
location /api {
proxy_pass http://127.0.0.1:3000/api;
}
location /explorer {
proxy_pass http://127.0.0.1:3000/explorer;
}
}
and my loopback server serves static files like this:
app.use(loopback.static(path.resolve(__dirname, '../client')));
and at the end of my server.js file I serve the index.html file for any path not processed so angular processes that path in the front end
function startsWith(string, array) {
for(let i = 0; i < array.length; i++)
if(string.startsWith(array[i]))
return true;
return false;
}
let ignoredPaths = ['/css', '/fonts', '/img', '/js', '/lib', '/sfx', '/views', '/api'];
app.all('/*', function(req, res, next) {
//Redirecting to index only the requests that do not start with ignored paths
console.log("Requested url: ",req.url);
if(!startsWith(req.url, ignoredPaths)) {
res.sendFile('index.html', {root: path.resolve(__dirname, '../', 'client')});
} else {
next();
}
});
The issue is that I can only access http://www.example.com/ and it takes a lot to load and the url flashes many times like it's redirecting a lot of times, I redirect in front-end with angular to /home, I have the angular routing to use html5 to remove the /#!/ from the path, but I always get a Bad Gateway error if I try to access any other path, say http://www.example.com/admin/login and it's a nginx error, it's not reaching loopback so it can serve the index file. I've tried the try_files $uri $uri/ /index.html; but if I try to access any other custom path, say /auth/facebook it forces it to load it through index.html and that's a custom path I have setup on loopback to do special behaviour. I have no idea how to solve this, I just used that template config file, all I need is for it to redirect all requests from example.com to my app in localhost:3000 and do the same in the opposite direction.
So i kept trying, and tried again using the try_files with a #path (I guess that's kind of a label or something) and what I was missing when I tried it before was to attach $uri at the end of the proxy_pass
It ended up like this:
location / {
try_files $uri $uri/ #proxy;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_bind 127.0.0.1;
proxy_pass http://127.0.0.1:3000/;
}
location #proxy {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_bind 127.0.0.1;
proxy_pass http://127.0.0.1:3000$uri;
}
And all works dandy now :P.

How to handle redirects in Express application with Reverse Proxy

I have two Express powered NodeJS applications that are being served by Nginx. One application is a RESTful API, that is consumed by an Angular SPA and the other one is an Admin portal. This is something I'm trying to achieve here:
location / {
# Serves Client Side Angular Application
}
location /api {
# Serves RESTful Application
}
location /admin {
# Serves Admin Portal
}
Here is my complete configuration for nginx:
server {
server_name localhost;
listen 80;
listen [::]:80;
index index.html;
location / {
expires -1;
add_header Pragma "no-cache";
add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
root /var/www/example/client/dist;
try_files $uri $uri/ /index.html =404;
}
location /admin/ {
proxy_pass http://127.0.0.1:3010/;
proxy_http_version 1.1;
rewrite /admin/(.*) /$1 break;
proxy_redirect off;
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/admin/;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api {
proxy_pass http://127.0.0.1:3011;
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Both of the NodeJS application utilizes forever and run on their defined ports, perfectly.
The problem I'm facing with my admin application, when I try to hit http://example.com/admin it redirects me to http://example.com/login when it should redirect me to http://example.com/admin/login. I tried to solve this issue by adding a middleware to the admin application, like:
app.use('*', function (req, res, next) {
if(process.env.target === 'dev'){
if(req.originalUrl.indexOf('/admin') > -1){
res.redirect('/admin' + req.url);
}
next();
}
});
But this doesn't work. How should I handle this situation? Is there anyway nginx can handle application redirects? If not, how should I handle it in application for different environments(Dev, Staging, Prod etc)? I'm using nginx 1.4.6, node 0.12.2 and express 4.12.2. I have no issue if I have to add any 3rd party module in my application but I'd prefer if there is an nginx solution for my problem.
Thanks.
The problem with your nginx config is the rewrite for the /admin route:
rewrite /admin/(.*) /$1 break;
With this rule in place your admin portal node app doesn't know that its paths are all hosted off of a /admin root, so if your app does a redirect relative what it thinks is root, eg res.redirect('/login'), it makes sense that it would send the browser to localhost/login.
An alternative would be to remove your nginx rewrite and just make the admin portal aware of this /admin path. That way you have the option of redirecting to a relative path, like this: res.redirect('login') which tells express to take the current request's path into account. You can use the express.Router to make this cleaner, like this for example:
var adminRoutes = express.Router()
.get('/', function(req, res) {
if(!req.user) {
// Will redirect to /admin/login
res.redirect('login');
} else {
res.send('Hello');
}
})
.get('/login', login);
// Tell express to host all routes above under a common /admin root
app.use('/admin', adminRoutes);
If on the other hand there's a better reason why you must have the nginx rewrite, you can always just include the /admin root manually for all of your app's redirects. You could provisionally append this mount path by reading it from a custom header set byproxy_set_header. For example:
app.use(function(req, res, next) {
res.mountedAt = req.headers['X-Mounted-At'];
next();
});
app.get('/', function(req, res) {
res.redirect(path.join(res.mountedAt, '/login'));
});
I solved this by combining Andrew Lavers's first solution with
location /admin/ {
proxy_pass https://127.0.0.1:3010/admin/;
}

Resources