nextJS + ExpressJS with Apache reverse proxy results in "undefined" in url - node.js

I'm trying to setup a nextJS + ExpressJS site on Apache. I've got a reverse proxy which forwards any request to http://1.23.45.67 (my ip at port 80) to localhost:3000 where my nodejs server is running. When I pull up the site in a browser everything looks fine. Images/css loads properly and I can navigate around the site but whenever an ajax request is made to the backend (e.g. api call to register a user) it does this with the request url http://1.23.45.67/undefined/api/v1/users/register.
My virtual host apache config looks like
<VirtualHost *:80>
ServerName www.myservername.com
ServerAlias myservername.com
DocumentRoot /var/www/myapp/html
ErrorLog /var/www/myapp/log/error.log
CustomLog /var/www/myapp/log/requests.log combined
ProxyRequests on
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
</VirtualHost>
I'm generating my ajax request url using a base url variable from a .env file.
I've also tried http://localhost/ and http://localhost:3000/ as the BASE_URL
# simplified .env file
...
BASE_URL=http://1.23.45.67/
...
# simplified register page where we make the ajax request.
...
fetch(`${process.env.BASE_URL}/api/v1/users/register`, {method: 'GET'});
...

For anyone who stumbles upon this...It's not an issue with apache config. The issue is how I'm using dotenv in nextjs. With my current project nextjs is grabbing the .env variables at buildtime and not runtime. The .env doesn't exist when when my github action compiles and pushes my code to the server so I get "undefined" in the url. I believe NextJS's publicRuntimeConfig is the solution. All .env variables used on the frontend need to be added as publicRuntimeConfig variables if you want them to be grabbed at runtime and not buildtime. https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration

Related

Reverse Proxy Httpd Nodejs Pm2

I'm trying to set up a reverse proxy to a Node.Js Express web server. Basically I'm trying to redirect any traffic coming to mywebsite.com and have httpd redirect them to node.js app endpoints. So if I would do mywebsite.com/test then it would run the /test function in my node app.
I'm running on EC2 (Amazon Linux 2 AMI)
My backend is at:
/home/ec2-user/mywebsite/mywebsite_backend/dist
I'm running the with pm2, which is a monitoring software for node.
Here's the configuration to /etc/httpd/conf.d/mywebsite.com.conf
<VirtualHost *:80>
ServerName mywebsite.com
ServerAlias www.mywebsite.com
ProxyPreserveHost On
ProxyPass /home/ec2-user/mywebsite/mywebsite_backend/dist http://localhost:8055/
ProxyPassReverse /home/ec2-user/mywebsite/mywebsite_backend/dist http://localhost:8055/
ErrorLog /etc/httpd/conf.d/error.log
CustomLog /etc/httpd/conf.d/custom.log combined
DocumentRoot /home/ec2-user/mywebsite/mywebsite_backend/dist
</VirtualHost>
When I try to open: https://mywebsite.com from a browser I get apache testpage.
When I try https: https://mywebsite.com/test' I get "Not Found". I'm guessing that this is because there's no :443 virtualhost?
When I try http aka non-secure: http://mywebsite.com I get "Forbidden".
Edit1: It seems that Apache is by default looking in /var/www/html. I added a index.html page there and now I can see it when visiting mywebsite.com.
Edit2:
When doing curl http://localhost:8055/test on the server the response is:
curl: (52) Empty reply from server
When doing curl https://localhost:8055/test on the server the response is:
curl: (60) SSL: no alternative certificate subject name matches target
host name 'localhost' More details here:
https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could
not establish a secure connection to it. To learn more about this
situation and how to fix it, please visit the web page mentioned
above.
I've made the SSL certificates for my express server with Let's encrypt and I'm pointing to the generated files in my code and it works locally, but it seems I have a problem with SSL with apache?
Edit3:
curl -k http://localhost:8055/test
Does not work.
I've tried now pingin my node app with
curl -k https://localhost:8055/test
It works.
Edit4: Looking at the error logs /etc/httpd/conf.d/error.log it seems that there are permission problems with the code being in /home directory. I will move it to /var/www/html/

POST becomes GET after ProxyPass

I have a nodejs app running inside a docker container on port 3050. If I allow the port through the firewall then everything works fine. But if I try to ProxyPass it then the app seems to receive GET instead of POST.
ProxyPass / http://localhost:3050/
When I look at the access log apache receives the request as a POST. But logging the req.method in expressjs results in GET. I have also tried loads of other settings
ProxyRequests Off
ProxyPreserveHost On
ProxyPass / http://localhost:3050/
ProxyPassReverse / http://localhost:3050/
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
The strangest thing is that I am Proxying lots of other services like gitlab and keycloak without any problem.
Could it be something to do with HTTP 1.1 or HTTP 2? Or does my httpd server have some setting messed up?
I was only testing with postman...obviously in future I should try curl or something as well. Turns out that postman has a setting "Redirect with the original HTTP method instead of the default behavior of redirecting with GET." which is turned off by default. I don't know whether the browser will behave like this but at least now it seems to be working
update:
Actually it wasn't even that setting. it was because i had not prefixed the url with https:// due to a training course which excluded the protocol because we weren't working with secure. so the httpd server proxied port 80 to 443 and postman didn't handle it correct. as i would be hitting directly https in production it shouldn't be a problem

Apache Reverse Proxy and localhost - "Mixed content, the content must be served over HTTPS"

I have created a reverse proxy for my node server that runs on localhost, so that it can be served over HTTPS.
The forwarding works grate, however when the app tries to make requests I get:
Mixed Content: The page at 'https://foo.com/' was loaded over HTTPS,
but requested an insecure XMLHttpRequest endpoint
'http://localhost:8888/graphql?query=%7Bnotifications(userid)%7Bid%2C…
This request has been blocked; the content must be served over HTTPS.
Vhost config:
<VirtualHost *:443>
ServerName www.foo.com
ServerAlias foo.com
DocumentRoot /var/www/foo/
ErrorLog /var/www/foo/error.log
CustomLog /var/www/foo/requests.log combined
SSLEngine on
SSLProtocol all -SSLv2
SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5
SSLCertificateFile "/etc/letsencrypt/live/foo.com/cert.pem"
SSLCertificateKeyFile "/etc/letsencrypt/live/foo.com/privkey.pem"
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://localhost:8888/
ProxyPassReverse / http://localhost:8888/
</VirtualHost>
What is missing from my setup?
You are openning the page on https://foo.com/, but URLs within your page contain hardcoded localhost domain and port. While rendering the page, client browser will try to fetch 'http://localhost:8888/graphql effectively skipping apache (which is running on port 80, on server foo.com) and hitting directly your node app, which will 1) work only if you run the browser from the very same machine where you have your node app running, and 2) even then, you will get the above error since some page assets are loaded using http.
When you use relative URLs (for example URL that begins with /), browser will prepend the base URL, resulting in https://foo.com/graphql.
Absolute vs relative URLs
You need to add a SSL certificate to your node.js app. Enabling it on apache won't help since the apache is forwarding the requests to your node.js app on port 8888 (which communicates on plain http and not https). That's why you get the mixed content error. The initial request is on https on apache then forwarded to http to node.js
Steps to configure node.js app with a SSL certificate (you can use a self-signed certificate or a commercial one).
First you have to use ssl-root-cas available via npm. The configure it as follows:
'use strict';
var https = require('https')
, cas
;
// This will add the well-known CAs
// to `https.globalAgent.options.ca`
require('ssl-root-cas').inject();
cas = https.globalAgent.options.ca;
cas.push(fs.readFileSync(path.join(__dirname, 'ssl', '01-ssl-intermediary-a.pem')));
cas.push(fs.readFileSync(path.join(__dirname, 'ssl', '02-ssl-intermediary-b.pem')));
cas.push(fs.readFileSync(path.join(__dirname, 'ssl', '03-ssl-site.pem')));
Try and see if that works!

Ubuntu - Apache - Reverse Proxy - NodeJS 302 : moved permanently on post method

I have an application running on my VPS at http://localhost:3000.
I'm trying to make a reverse Proxy with Apache2 to make it accessible at node.mydomain.net.
With that virtualhost config :
<VirtualHost *:80>
ProxyPreserveHost On
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
ServerName node.mydomain.net
ServerAlias www.node.mydomain.net
</VirtualHost>
It's actually working with the welcome page, but when i'm trying to connect, the POST method to node.mydomain.net/authenticate, it's actually return in the web inspector :
request method : POST Status Code : 302 Moved Temporarily.
Im' not sure about the real problem and I found no solution by the moment. Can it a rewriting problem ? or a POST method not allowed problem ?
If anyone can send me on a serious trail, i'll be very happy.
Many thanks.

Apache Proxy for Node.js and Wordpress on same "Domain" but different paths

Here is the case.
I have a Wordpress website running on my main domain say http://www.example.com.
.htaccess is enabled on server and I am using Wordpress permalinks to generate pretty URLs.
Now I have another application in Node.js (Express) which I have deployed on same server and is running on port number 3000. So if I put http://www.example.com:3000 it triggers my Expressjs app. and it is running flawlessly.
What I want is to run the Node.js application on a specific path from the same domain. For example, if I hit, http://www.example.com/node it should take user to my Node.js app which it is taking quite fine if I use Apache mod_proxy. But this works only for the / route of my Expressjs router.
But I also have several other routes e.g. /subscribe, /confirm but none of them work.
Following is my Apache config which I tried to make work by giving multiple ProxyPass directives but it doesn't work.
ProxyPass /node http://127.0.0.1:3000/
ProxyPassReverse /node http://127.0.0.1:3000/
ProxyPass /node/subscribe http://127.0.0.1:3000/subscribe
ProxyPassReverse /node/subscribe http://127.0.0.1:3000/subscribe
The Express router is pretty basic and uses following app configuration:
var router = express.Router();
app.use("/",router);
router.get("/",function(req,res){
res.sendFile(templatepath + "index.html");
});
router.get("/subscribe",function(req,res){
res.sendFile(templatepath + "subscribe.html");
});
router.get("*",function(req,res){
res.sendFile(templatepath + "404.html");
});
When I try to load http://www.example.com/node/subscribe it simply loads the * route and displays the 404.html template.
Finally got it working by modifying the Apache configuration file as follows:
ProxyPreserveHost On
RewriteEngine On
ProxyPass ^/node/(.*) http://127.0.0.1:3000/$1 [P,L]
ProxyPassReverse /node/ http://127.0.0.1:3000/

Resources