I am running Node and Apache on the same server, where node is the backend server, requested by Axios to collect user data from the front end.
I used Apache to request an SSL certificate through certbot
and was successful. I am trying to deploy node backend to access my endpoint
ie (website.com/endpoint)
.
I am able to see the test index.html
, located in the website folder. When I try aws.website.com/endpoint
I get the server time out and the 404 not found error.
The location of my app in the Linux server is var/www/website.com
instead of the default var/www/html
path.
My question: How can I run node and apache on the same server to allow the user to access the app through https?
***UPDATE: you need the node app to run on a separate port in my case 3001, and Apache to run on a separate port, ie 80, and use a reverse proxy via mod-proxy.
Here is the 000-default.conf
file:
<VirtualHost *:80>
ProxyPreserveHost On
ProxyPass / http://18.191.235.31/
ProxyPassReverse / http://18.191.235.31/
ServerName aws.website.com
ServerAdmin example@localhost
DocumentRoot /var/www/aws.backend-dg.com
</VirtualHost>
Here are my config
files for the Apache virtual host:
website/com.conf
:
<VirtualHost *:80> ServerName aws.website.com ... DocumentRoot /var/www/aws.website.com ... RewriteEngine on RewriteCond %{SERVER_NAME} =aws.website.com RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] </VirtualHost>
Here is my ‘website.com.le-ssl.conf’ (manually generated by certbot)`
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName aws.website.com
....
DocumentRoot /var/www/aws.website.com
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLCertificateFile /etc/letsencrypt/live/website/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/website/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>
Here is my default-ssl.conf
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
DocumentRoot /var/www/aws.backend-dg.com
....
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
....
SSLEngine on
....
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
....
</VirtualHost>
</IfModule>
App.js file
const cors = require("cors");
const mongoose = require("mongoose");
const express = require("express");
const logger = require("morgan");
const app = express();
require("./models/Email");
const routes = require("./routes/routes");
app.use(cors());
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "DELETE, PUT, GET, POST");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Express only serves static assets in production
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
}
app.get('*',(req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
app.use(express.json());
app.use(logger("dev"));
app.use("/", routes);
app.use("/search", express.static("search"));
const mongoURI =
"mongodb+srv://somename@cluster................";
const conn = mongoose.createConnection(mongoURI);
mongoose.connect(
mongoURI,
{ useNewUrlParser: true },
{ useUnifiedTopology: true }
);
conn.once("open", () => {
console.log("Connection Successful");
});
conn.on("error", console.error.bind(console, "MongoDB connection error:"));
const server = app.listen(3001, () => {
console.log(`Express running PORT ${server.address().port}`);
});
const express = require("express");
const router = express.Router();
const emailController = require('../controllers/EmailController');
router.get('/', emailController.baseRoute);
router.post('back/get-email', emailController.createEmail);
module.exports = router;
The answer is to secure the node backend server in order for the user to stay on the secure HTTPS
address. You do this by assigning SSL
keys to the node server. Then edit the app.js file to connect to HTTPS
, not HTTP
. The above app.js
connects to HTTP
only. So this needed to be fixed. Finally, edit your reverse proxies in the Apache
virtual hosts to fwd HTTP
to HTTPS
.
In my case, the frontend was secure via HTTPS
SSL
certificates from let's encrypt (certbot
) ie https://website.com
, but the backend was not secure, so when I tried to go to https://websote/endpoint
there was an error.
The reason for this was because the backend app.js
file connected to an HTTP
server when it needed to connect to HTTPS
.
Note: if you have a secure website/app and you make an Axios request to a non-secure HTTP
address then you will get a cors
error stating you need to change HTTP
to HTTPS
. You do this by making the node.js server secure.
1a. Create a separate directory in the node folder to hold the certs. sudo mkdir directory_name
1b. *Copy SSL
certificates from the Apache server to the Node server directory.
sudo cp etc/letsencrypt/live/your_webiste_folder/privkey.pem /var/www/directory_name
sudo cp /etc/letsencrypt/live/your_website_folder/fullchain.pem /var/www/directory_name
click here for guide about copying files and its contents
1c Assign permission rules to that folder. I used ubuntu
as my group because root
had the ownership of the directory and the keys.
click this link to create a group and learn about permissions
-step a. change ownership of group that owns the key files
-NOTE: you need to do this with root
, type the following cmd
: sudo -i
chown group_name /var/path_to_webiste/cert_directory
-step b. change ownership of keys in group
chown group_name /var/path_to_website/privkey.pem
,
chown group_name /var/path_to_website/fullchain.pem
,
1d Check to see if permissions were applied successfully
ls -l
-Below 3rd line is an example of permissions of directory to root
-Below 4th line is an example of permissions of directory to group_name (ubuntu). You want to see this line in your code.
root@ip-123-45-67-890:/var/www# ls -l total 8 drwxr-xr-x 6 root root 4096 Oct 20 23:50 directory_name drwxr-xr-x 2 ubuntu ubuntu 4096 Oct 20 16:10 directory_name
2. Edit app.js
or server.js
to connect to HTTPS
server, NOT HTTP
Note: when you provide the location paths to the SSL certificates directories in the Node
folder in app.js
or server.js
, Node
will NOT be able to see them, this is why you need to assign permissions.
App.js:
//imports
......
//use node over secure server
const https = require("https"),
fs = require("fs");
const sslServer = https.createServer (
{
key: fs.readFileSync('/var/path_to_privkey.pem'),
cert: fs.readFileSync('/var/path_to_fullchain.pem'),
},
app <---MAKE SURE TO PASS APP IN
)
......
......
sslServer.listen(3000, () => {
console.log(`Express running PORT 3000 working!`)
});
Click here for a walkthrough process for above app.js code
Assign firewall permission rules to allow the port
you are using from the app.js
file ie 3001
to be allowed through the command ufw
.
-note: if you try to connect to the port
and you get a timeout, then your firewall settings for apache are not allowing the port to come through, this is why you need to edit the apache firewall permissions
sudo ufw allow 3001
Edit 000-default.conf
, website.com.conf
, and website.com-le-ssl
files to fwd from HTTP to HTTPS.
-note: website.com.conf-le-ssl
is generated by certbot
from letsencrypt
000-default.conf:
<VirtualHost *:80>
ServerName website.com
ServerAlias www.website.com
DocumentRoot /var/www/website.com
ProxyPreserveHost On
ProxyRequests Off
ProxyPass /get-email https://1.234.567.890:3001/
ProxyPassReverse /get-email https://1.234.567.890:3001/
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
website.com.conf:
<VirtualHost *:80>
ServerName website.com
ServerAlias www.website.com
DocumentRoot /var/www/aws.backend-dg.com
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
ProxyPreserveHost Off
ProxyRequests Off
ProxyPass / http://1.234.567.890:3001/
ProxyPassReverse / https://1.234.567.890:3001/
RewriteEngine on
RewriteCond %{SERVER_NAME} =aws.backend-dg.com [OR]
RewriteCond %{SERVER_NAME} =3.144.239.224:3001 [OR]
RewriteCond %{SERVER_NAME} =aws.backend-dg.com:3001 [OR]
RewriteCond %{SERVER_NAME} =www.aws.backend-dg.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
website.com-le-ssl.conf:
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName website.com
ServerAlias www.website.com
DocumentRoot /var/www/website.com
SSLProxyEngine On
ProxyPass /get-email http://1.234.567.890:3001/
ProxyPassReverse /get-email https://1.234.567.890:3001/
ProxyPreserveHost Off
ProxyRequests Off
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/website.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/website.com/privkey.pem
</VirtualHost>
</IfModule>