node.jshttpssocket.iohttp-status-code-403

How to load socketio over https from ssl website?


I have a HTTP nodejs/socketio server running on port 3001 successfully. I'm trying to access the server from a SSL website encrypted with certbot (let's encrypt).

When I visit https//:example.com/index.html, I see 404 error :

GET https://www.example.com:8000/socket.io/socket.io.js net::ERR_ABORTED 404 (Not Found)

I've been reading about configuring apache with mod_proxy here and here. HOWEVER, according to nodejs documentation, all is needed is pem files which I have.

I feel like I'm chasing my own tail and I need help. I've been spending nearly a week doing research and trying different approaches. I'm back at square one. Anyone ran a https server and accessed it over SSL website successfully? Here all the relevant codes below.

server.js

const express = require('express');
const app = express();
const https = require('https');
const fs = require('fs');

// This line is from the Node.js HTTPS documentation.
var options = {
    key: fs.readFileSync('../ssl/privkey.pem'),//etc/letsencrypt/
    cert: fs.readFileSync('../ssl/cert.pem')
  };

const sslserver = https.createServer(options,app);

//establish connection
io.on('connection', (socket) => {

//requests

});

//listening for https
sslserver.listen(8000, () => {
console.log('listening on *: 8000');
});

index.html

<!DOCTYPE html>
<html>

<head>

<title>My Web App </title>

<meta charset="utf-8">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
<meta name="color-scheme" content="light dark">
</head>

<body >


<p>Welcome to web app</p>
<p>login now</p>

</body>

</html>
<!--SCRIPTS-->
<script src="cordova.js"></script>
<script src="https://www.example.com:8000/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect('https://www.example.com:8000', {'multiplex': false});
</script>

Solution

  • If you are using credentials in server.js, You should listen at port 443. Otherwise, you just need to listen at your custom port, then create a reverse proxy for it.

    for ex:

    server.js (directly listen to 443/80)

    const express = require('express');
    const app = express();
    var http = require('http');
    const https = require('https');
    const fs = require('fs');
    
    // This line is from the Node.js HTTPS documentation.
    var options = {
        key: fs.readFileSync('../ssl/privkey.pem'), //etc/letsencrypt/
        cert: fs.readFileSync('../ssl/cert.pem')
    };
    
    const port = process.env.PORT || 80;
    const sercure_port = 443;
    
    // use on server with https
    
    var httpServer = http.createServer((req, res) => {
        res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
        res.end();
    });
    http.createServer(function (req, res) {
        res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url});
    res.end();
    })
    var httpsServer = https.createServer(options, app);
    
    const io_server = require('socket.io')(httpsServer);
    
    httpServer.listen(port, () => {
        console.log('http server listening on port ' + port);
    });
    
    httpsServer.listen(sercure_port, () => {
        console.log('https server listening on port ' + sercure_port);
    });
    

    server.js (listen at custom port)

    const express = require('express');
    const app = express();
    const http = require('http');
    const fs = require('fs');
    var options = {
        key: fs.readFileSync('../ssl/privkey.pem'), //etc/letsencrypt/
        cert: fs.readFileSync('../ssl/cert.pem')
    };
    var port = process.env.PORT || 8080;
    
    http.createServer(function (req, res) {
        res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url});
        res.end();
    })
    var httpsServer = http.createServer(options, app);
    
    const io_server = require('socket.io')(httpsServer);
    
    httpsServer.listen(port, () => {
        console.log('https server listening on port ' + port);
    });
    

    NGINX reserve proxy

    server {
        listen       443 ssl http2;
        listen       [::]:443 ssl http2;
        server_name yourdomain.com www.yourdomain.com;
        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;
        
    
        location / {
            proxy_pass http://localhost:8080;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
        }
    
        ssl_certificate path-to-crt.crt;
        ssl_certificate_key path-to-key.key;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 
        ssl_prefer_server_ciphers on;
        ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    
       #Load configuration files for the default server block.
       include /etc/nginx/default.d/*.conf;
    
       error_page 404 /404.html;
           location = /40x.html {
       }
    
       error_page 500 502 503 504 /50x.html;
           location = /50x.html {
       }
    }