0

Trying to set up my Express server on HTTPS but unable to access my api. Here is my code:

// server.js
const express = require('express');
const { readFileSync } = require('fs');
const https = require('https');
const app = express();

const key = readFileSync(
  '/etc/letsencrypt/live/mydomain.com/privkey.pem',
  'utf8'
);
const cert = readFileSync(
  '/etc/letsencrypt/live/mydomain.com/fullchain.pem',
  'utf8'
);
const ca = readFileSync(
  '/etc/letsencrypt/live/mydomain.com/chain.pem',
  'utf8'
);
const credentials = { key, cert, ca };

const port = 443

const server = https.createServer(credentials, app);

server.listen(port, () => console.log(`Server is running on port ${port}`));

The error I am receiving is a 502 Bad gateway on all calls to the server.

Everything worked perfectly before when I was running on HTTP like this:

const server = app.listen(8000, () => {});

Beside the URL on my site I can see the padlock icon and it says my site is secure so I believe my certifcate is valid.

Have seen a lot of similar questions posted on here and followed what I've seen in responses. I've even asked chatGPT to check my code and it doesn't see any errors. I've also tried reading my SSL keys as .env variables so I don't think the issue is an incorrect file path.

Can anybody please help me find the solution?

EDIT

Here is an example of a get request I am making to my backend using axios:

const fetchMembers = async () => {
    await axios
      .get(`/api/total-users`)
};

This invokes a function totalUsers with express.Router from a users file in my routes folder on the backend:

// routes/users.js
const express = require('express');
const router = express.Router();

router.get('/total-users', totalUsers);

const totalUsers = async (req, res) => {
  try {
    const total = await User.find().estimatedDocumentCount();
    res.json(total);
  } catch (err) {
    console.log(err);
  }
};

I am using a middleware app.use with a prefix /api and importing the users file to make a connection to my endpoints:

// server.js
const userRoutes = require('./routes/users')

app.use('/api', userRoutes)

While editing this question I tried modifying the middleware prefix to `:443/api' however this still hasn't helped.

sgt_pepper85
  • 421
  • 4
  • 14
  • Bad gateway is typically something in the infrastructure between the client and server? Is there a proxy between your client and server that isn't properly configured for https? What port are you running the https server on? What is the client? – jfriend00 Feb 18 '23 at 17:16
  • Server is running on port 443 now, client is on 3000. I had ufw enabled but the error persists with this disabled also – sgt_pepper85 Feb 18 '23 at 17:26
  • What do you mean client is on 3000? For a client to make a request to your https server on port 443, the client has to use port 443 and has to make the request using the https protocol. – jfriend00 Feb 18 '23 at 17:51
  • I see. Could you please advise on how I might amend the port on the client side then? I'm using create-react-app and tried modifying my `start` command from `package.json` to `"start": “set PORT=443 && react-scripts start"` but the same error persists... – sgt_pepper85 Feb 18 '23 at 18:09
  • Please show the relevant client-side code that is making the request to your https server if you want help with the client. – jfriend00 Feb 18 '23 at 18:14
  • Thanks, I have edited my question to show request from the front end – sgt_pepper85 Feb 18 '23 at 18:58
  • Are you using React? Did you modify your `setupProxy.js` to forward requests to the new port? – Wesley LeMahieu Feb 18 '23 at 19:04
  • Yes I am using React, but I do not have a `setupProxy.js` file... – sgt_pepper85 Feb 18 '23 at 19:08
  • @sgt_pepper85 can you make a curl request to your backend directly to see if it's responding correctly? – arthur simas Feb 18 '23 at 19:15
  • Did you define `User` inside `routes/users.js` and just exclude that from your code sample? Otherwise `User` is not defined it seems. – Wesley LeMahieu Feb 18 '23 at 19:51

1 Answers1

0

Where is User defined inside of routes/users.js? You need to import your DB models before accessing them like this:

const total = await User.find().estimatedDocumentCount();

Otherwise, generally when running fetch('/api...), React will try to access localhost:3000/api instead of localhost:443/api.

React needs to know where to proxy requests to the backend. One way is using the http-proxy-middleware library and by defining a setupProxy.js file.

client/src/setupProxy.js

/**
 * Proxy most calls to the server
 * @type {Array}
 */
/* eslint-disable @typescript-eslint/no-var-requires */
const { createProxyMiddleware } = require('http-proxy-middleware');

const proxy_urls = ['/api/*';

const target = 'https://localhost:443';

module.exports = function (app) {
  proxy_urls.forEach((url) => {
    app.use(url, createProxyMiddleware({ target }));
  });
};
Wesley LeMahieu
  • 2,296
  • 1
  • 12
  • 8
  • could you elaborate why `http-proxy-middleware` is needed? the snipped is confusing – arthur simas Feb 18 '23 at 19:36
  • Are you having the issue in development and production or just dev? https://create-react-app.dev/docs/proxying-api-requests-in-development/ – Wesley LeMahieu Feb 18 '23 at 19:40
  • like OP said: "server is running on port 443 now, client is on 3000". so there's no need to use a proxy. AFAIK, his use case is different from the one of the link you've provided – arthur simas Feb 18 '23 at 19:44
  • If there is no proxy, React will try to hit `localhost:3000/api` when running `fetch('/api...'`) instead of `localhost:443/api`. – Wesley LeMahieu Feb 18 '23 at 19:46
  • i would do this https://stackoverflow.com/a/42459782/20269772 instead of using a proxy. but i got your point – arthur simas Feb 18 '23 at 19:51
  • That doesn't scale well when you need to proxy multiple endpoints i.e (`/api`, `/auth`, `/external-api`, `/logout`) and potentially to different back-ends. It also relies on setting a `REACT_` variable, then using that variable in your fetch calls like `fetch(${REACT_APP_SOME_URL/your-endpoint)`. A much more elegant and scalable way which I use across many apps is just using one of two of the CRA's documented ways of proxying dev API requests to the backend. Then `fetch(/api)` works the same in both DEV and PROD without having to juggle unnecessary `REACT_` env variables. – Wesley LeMahieu Feb 18 '23 at 20:12
  • 1
    OP is not using `fetch`. in `axios` you don't have this kind of issue because we use [`axios.create`](https://github.com/axios/axios#creating-an-instance). you can just give it the `baseURL`, each instance can handle multiple endpoints. and if you have multiple backends, just create more instances – arthur simas Feb 18 '23 at 20:25
  • `axios.create` sounds very useful in that case. Thanks for the tip! – Wesley LeMahieu Feb 18 '23 at 20:30