Given an SSL key and certificate, how does one create an HTTPS service?
The Express API doc spells this out pretty clearly.
Additionally this answer gives the steps to create a self-signed certificate.
I have added some comments and a snippet from the Node.js HTTPS documentation:
var express = require('express');
var https = require('https');
var http = require('http');
var fs = require('fs');
// This line is from the Node.js HTTPS documentation.
var options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.cert')
};
// Create a service (the app object is just a callback).
var app = express();
// Create an HTTP service.
http.createServer(app).listen(80);
// Create an HTTPS service identical to the HTTP service.
https.createServer(options, app).listen(443);
For Node 0.3.4 and above all the way up to the current LTS (v16 at the time of this edit), https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener has all the example code you need:
const https = require(`https`);
const fs = require(`fs`);
const options = {
key: fs.readFileSync(`test/fixtures/keys/agent2-key.pem`),
cert: fs.readFileSync(`test/fixtures/keys/agent2-cert.pem`)
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end(`hello world\n`);
}).listen(8000);
Note that if want to use Let's Encrypt's certificates using the certbot tool, the private key is called privkey.pem and the certificate is called fullchain.pem:
const certDir = `/etc/letsencrypt/live`;
const domain = `YourDomainName`;
const options = {
key: fs.readFileSync(`${certDir}/${domain}/privkey.pem`),
cert: fs.readFileSync(`${certDir}/${domain}/fullchain.pem`)
};
Found this question while googling "node https" but the example in the accepted answer is very old - taken from the docs of the current (v0.10) version of node, it should look like this:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
The above answers are good but with Express and node this will work fine.
Since express create the app for you, I'll skip that here.
var express = require('express')
, fs = require('fs')
, routes = require('./routes');
var privateKey = fs.readFileSync('cert/key.pem').toString();
var certificate = fs.readFileSync('cert/certificate.pem').toString();
// To enable HTTPS
var app = module.exports = express.createServer({key: privateKey, cert: certificate});
The minimal setup for an HTTPS server in Node.js would be something like this :
var https = require('https');
var fs = require('fs');
var httpsOptions = {
key: fs.readFileSync('path/to/server-key.pem'),
cert: fs.readFileSync('path/to/server-crt.pem')
};
var app = function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}
https.createServer(httpsOptions, app).listen(4433);
If you also want to support http requests, you need to make just this small modification :
var http = require('http');
var https = require('https');
var fs = require('fs');
var httpsOptions = {
key: fs.readFileSync('path/to/server-key.pem'),
cert: fs.readFileSync('path/to/server-crt.pem')
};
var app = function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}
http.createServer(app).listen(8888);
https.createServer(httpsOptions, app).listen(4433);
Update
Use Let's Encrypt via Greenlock.js
Original Post
I noticed that none of these answers show that adding a Intermediate Root CA to the chain, here are some zero-config examples to play with to see that:
https://github.com/solderjs/nodejs-ssl-example
http://coolaj86.com/articles/how-to-create-a-csr-for-https-tls-ssl-rsa-pems/
https://github.com/solderjs/nodejs-self-signed-certificate-example
Snippet:
var options = {
// this is the private key only
key: fs.readFileSync(path.join('certs', 'my-server.key.pem'))
// this must be the fullchain (cert + intermediates)
, cert: fs.readFileSync(path.join('certs', 'my-server.crt.pem'))
// this stuff is generally only for peer certificates
//, ca: [ fs.readFileSync(path.join('certs', 'my-root-ca.crt.pem'))]
//, requestCert: false
};
var server = https.createServer(options);
var app = require('./my-express-or-connect-app').create(server);
server.on('request', app);
server.listen(443, function () {
console.log("Listening on " + server.address().address + ":" + server.address().port);
});
var insecureServer = http.createServer();
server.listen(80, function () {
console.log("Listening on " + server.address().address + ":" + server.address().port);
});
This is one of those things that's often easier if you don't try to do it directly through connect or express, but let the native https module handle it and then use that to serve you connect / express app.
Also, if you use server.on('request', app) instead of passing the app when creating the server, it gives you the opportunity to pass the server instance to some initializer function that creates the connect / express app (if you want to do websockets over ssl on the same server, for example).
To enable your app to listen for both http and https on ports 80 and 443 respectively, do the following
Create an express app:
var express = require('express');
var app = express();
The app returned by express() is a JavaScript function. It can be be passed to Node’s HTTP servers as a callback to handle requests. This makes it easy to provide both HTTP and HTTPS versions of your app using the same code base.
You can do so as follows:
var express = require('express');
var https = require('https');
var http = require('http');
var fs = require('fs');
var app = express();
var options = {
key: fs.readFileSync('/path/to/key.pem'),
cert: fs.readFileSync('/path/to/cert.pem')
};
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
For complete detail see the doc
You can use also archive this with the Fastify framework:
const { readFileSync } = require('fs')
const Fastify = require('fastify')
const fastify = Fastify({
https: {
key: readFileSync('./test/asset/server.key'),
cert: readFileSync('./test/asset/server.cert')
},
logger: { level: 'debug' }
})
fastify.listen(8080)
(and run openssl req -nodes -new -x509 -keyout server.key -out server.cert to create the files if you need to write tests)
If you need it only locally for local development, I've created utility exactly for this task - https://github.com/pie6k/easy-https
import { createHttpsDevServer } from 'easy-https';
async function start() {
const server = await createHttpsDevServer(
async (req, res) => {
res.statusCode = 200;
res.write('ok');
res.end();
},
{
domain: 'my-app.dev',
port: 3000,
subdomains: ['test'], // will add support for test.my-app.dev
openBrowser: true,
},
);
}
start();
It:
Will automatically add proper domain entries to /etc/hosts
Will ask you for admin password only if needed on first run / domain change
Will prepare https certificates for given domains
Will trust those certificates on your local machine
Will open the browser on start pointing to your local server https url
Download rar file for openssl set up from here: https://indy.fulgan.com/SSL/openssl-0.9.8r-i386-win32-rev2.zip
Just copy your folder in c drive.
Create openssl.cnf file and download their content from : http://web.mit.edu/crypto/openssl.cnf
openssl.cnf can be put any where but path shoud be correct when we give in command prompt.
Open command propmt and set openssl.cnf path C:\set OPENSSL_CONF=d:/openssl.cnf
5.Run this in cmd : C:\openssl-0.9.8r-i386-win32-rev2>openssl.exe
Then Run OpenSSL> genrsa -des3 -out server.enc.key 1024
Then it will ask for pass phrases : enter 4 to 11 character as your password for certificate
Then run this Openssl>req -new -key server.enc.key -out server.csr
Then it will ask for some details like country code state name etc. fill it freely.
10 . Then Run Openssl > rsa -in server.enc.key -out server.key
Run this OpenSSL> x509 -req -days 365 -in server.csr -signkey server.key -out server.crt then use previous code that are on stack overflow
Thanks
I'm using node and socket.io to write a chat application. It works fine on Chrome but mozilla gives an error to enable the Cross-Origin Requests.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://waleedahmad.kd.io:3000/socket.io/?EIO=2&transport=polling&t=1401964309289-2&sid=1OyDavRDf4WErI-VAAAI. This can be fixed by moving the resource to the same domain or enabling CORS.
Here's my code to start node server.
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
path = require('path');
server.listen(3000);
app.get('/', function(req, res) {
res.sendfile(__dirname + '/public/index.html');
});
On the client side.
var socket = io.connect('//waleedahmad.kd.io:3000/');
Script tag on HTML page.
<script type="text/javascript" src="//waleedahmad.kd.io:3000/socket.io/socket.io.js"></script>
I'm also using .htaccess file in the app root directory. (waleedahmad.kd.io/node).
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
Simple Server-Side Fix
❗ DO NOT USE "socketio" package... use "socket.io" instead. "socketio" is out of date. Some users seem to be using the wrong package.
❗ SECURITY WARNING: Setting origin * opens up the ability for phishing sites to imitate the look and feel of your site and then have it work just the same while grifting user info. If you set the origin, you can make their job harder, not easier. Also looking into using a CSRF token as well would be a great idea.
socket.io v3
docs: https://socket.io/docs/v3/handling-cors/
cors options: https://www.npmjs.com/package/cors
const io = require('socket.io')(server, {
cors: {
origin: '*',
}
});
socket.io < v3
const io = require('socket.io')(server, { origins: '*:*'});
or
io.set('origins', '*:*');
or
io.origins('*:*') // for latest version
* alone doesn't work which took me down rabbit holes.
I am using v2.1.0 and none of the above answers worked for me.
This did though:
import express from "express";
import http from "http";
const app = express();
const server = http.createServer(app);
const sio = require("socket.io")(server, {
handlePreflightRequest: (req, res) => {
const headers = {
"Access-Control-Allow-Headers": "Content-Type, Authorization",
"Access-Control-Allow-Origin": req.headers.origin, //or the specific origin you want to give access to,
"Access-Control-Allow-Credentials": true
};
res.writeHead(200, headers);
res.end();
}
});
sio.on("connection", () => {
console.log("Connected!");
});
server.listen(3000);
You can try to set origins option on the server side to allow cross-origin requests:
io.set('origins', 'http://yourdomain.com:80');
Here http://yourdomain.com:80 is the origin you want to allow requests from.
You can read more about origins format here
For anyone looking here for new Socket.io (3.x) the migration documents are fairly helpful.
In particular this snippet:
const io = require("socket.io")(httpServer, {
cors: {
origin: "https://example.com",
methods: ["GET", "POST"],
allowedHeaders: ["my-custom-header"],
credentials: true
}
});
If you are getting io.set not a function or io.origins not a function, you can try such notation:
import express from 'express';
import { Server } from 'socket.io';
const app = express();
const server = app.listen(3000);
const io = new Server(server, { cors: { origin: '*' } });
I tried above and nothing worked for me. Following code is from socket.io documentation and it worked.
io.origins((origin, callback) => {
if (origin !== 'https://foo.example.com') {
return callback('origin not allowed', false);
}
callback(null, true);
});
I just wanted to say that after trying a bunch of things, what fixed my CORS problem was simply using an older version of socket.io (version 2.2.0). My package.json file now looks like this:
{
"name": "current-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"devStart": "nodemon server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"socket.io": "^2.2.0"
},
"devDependencies": {
"nodemon": "^1.19.0"
}
}
If you execute npm install with this, you may find that the CORS problem goes away when trying to use socket.io. At least it worked for me.
In my case, I'm using an HTTP server and socket.io
Error:
var http = require('http').Server(app);
var io = require('socket.io')(http);
Solution:
var http = require('http').Server(app);
var io = require('socket.io')(http, { cors: { origin: '*' } });
client:
const socket = io('https://sms-server.cedrick1227.repl.co/', { });
server:
const io = new socket.Server(server, { cors: { origin: '*' } });
it works like a charm for me.
After read a lot of subjetcs on StakOverflow and other forums, I found the working solution for me. This solution is for working without Express.
here are the prerequisites.
call your js script (src=) form the same server the socket will be connected to (not CDN or local call)
ensure to have the same version of socket.io on server and client side
node modules required : fs, path, socket.io and winston for logging
Install Let's encrypt certbot and generate certificate for your domain or buy a SSL certificate
jQuery declared before socket.io.js on client side
UTF-8 encoding
SERVER SIDE
// DEPENDENCIES
var fs = require('fs'),
winston = require('winston'),
path = require('path');
// LOGS
const logger = winston.createLogger({
level : 'info',
format : winston.format.json(),
transports: [
new winston.transports.Console({ level: 'debug' }),
new winston.transports.File({ filename: 'err.log', level: 'err' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// CONSTANTS
const Port = 9000,
certsPath = '/etc/letsencrypt/live/my.domain.com/';
// STARTING HTTPS SERVER
var server = require('https').createServer({
key: fs.readFileSync(certsPath + 'privkey.pem'),
cert: fs.readFileSync(certsPath + 'cert.pem'),
ca: fs.readFileSync(certsPath + 'chain.pem'),
requestCert: false,
rejectUnauthorized: false
},
(req, res) => {
var filePath = '.' + req.url;
logger.info('FILE ASKED : ' + filePath);
// Default page for visitor calling directly URL
if (filePath == './')
filePath = './index.html';
var extname = path.extname(filePath);
var contentType = 'text/html';
switch (extname) {
case '.js':
contentType = 'text/javascript';
break;
case '.css':
contentType = 'text/css';
break;
case '.json':
contentType = 'application/json';
break;
case '.png':
contentType = 'image/png';
break;
case '.jpg':
contentType = 'image/jpg';
break;
case '.wav':
contentType = 'audio/wav';
break;
}
var headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
'Access-Control-Max-Age': 2592000, // 30 days
'Content-Type': contentType
};
fs.readFile(filePath, function(err, content) {
if (err) {
if(err.code == 'ENOENT'){
fs.readFile('./errpages/404.html', function(err, content) {
res.writeHead(404, headers);
res.end(content, 'utf-8');
});
}
else {
fs.readFile('./errpages/500.html', function(err, content) {
res.writeHead(500, headers);
res.end(content, 'utf-8');
});
}
}
else {
res.writeHead(200, headers);
res.end(content, 'utf-8');
}
});
if (req.method === 'OPTIONS') {
res.writeHead(204, headers);
res.end();
}
}).listen(port);
//OPENING SOCKET
var io = require('socket.io')(server).on('connection', function(s) {
logger.info("SERVER > Socket opened from client");
//... your code here
});
CLIENT SIDE
<script src="https://my.domain.com:port/js/socket.io.js"></script>
<script>
$(document).ready(function() {
$.socket = io.connect('https://my.domain.com:port', {
secure: true // for SSL
});
//... your code here
});
</script>
This could be a certification issue with Firefox, not necessarily anything wrong with your CORS. Firefox CORS request giving 'Cross-Origin Request Blocked' despite headers
I was running into the same exact issue with Socketio and Nodejs throwing CORS error in Firefox. I had Certs for *.myNodeSite.com, but I was referencing the LAN IP address 192.168.1.10 for Nodejs. (WAN IP address might throw the same error as well.) Since the Cert didn't match the IP address reference, Firefox threw that error.
Alright I had some issues getting this to work using a self signed cert for testing so I am going to copy my setup that worked for me. If your not using a self signed cert you probably wont have these issues, hopefully!
To start off depending on your browser Firefox or Chrome you may have different issues and I'll explain in a minute.
First the Setup:
Client
// May need to load the client script from a Absolute Path
<script src="https://www.YOURDOMAIN.com/node/node_modules/socket.io-client/dist/socket.io.js"></script>
<script>
var options = {
rememberUpgrade:true,
transports: ['websocket'],
secure:true,
rejectUnauthorized: false
}
var socket = io.connect('https://www.YOURDOMAIN.com:PORT', options);
// Rest of your code here
</script>
Server
var fs = require('fs');
var options = {
key: fs.readFileSync('/path/to/your/file.pem'),
cert: fs.readFileSync('/path/to/your/file.crt'),
};
var origins = 'https://www.YOURDOMAIN.com:*';
var app = require('https').createServer(options,function(req,res){
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', 'https://www.YOURDOMAIN.com:*');
res.setHeader('Access-Control-Request-Method', '*');
res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
res.setHeader('Access-Control-Allow-Headers', '*');
if ( req.method === 'OPTIONS' || req.method === 'GET' ) {
res.writeHead(200);
res.end();
return;
}
});
var io = require('socket.io')(app);
app.listen(PORT);
For development the options used on the client side are ok in production you would want the option:
rejectUnauthorized: false
You would more than likely want set to "true"
Next thing is if its a self signed cert you will need to vist your server in a separate page/tab and accept the cert or import it into your browser.
For Firefox I kept getting the error
MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
The solution for me was to add the following options and accepting the cert in a different page/tab.
{
rejectUnauthorized: false
}
In Chrome I had to open another page and accept the cert but after that everything worked fine with out having to add any options.
Hope this helps.
References:
https://github.com/theturtle32/WebSocket-Node/issues/259
https://github.com/socketio/engine.io-client#methods
I am facing problem while making an chat app using socket.io and node.js & React. Also this issue is not spacefic to Firefox browser, i face same issue in Edge & Chrome also.
"Cross-Origin request is blocked and it is used by some other resources..."
Then i download cors in project directory and put it in the server file index.js as below: To download simply type command using node.js :
npm install cors
const cors = require('cors');
app.use(cors());
This will allow CORS to used by different resources in the files and allow cross origin request in the browser.
For those using socket.io >= v4.4.0
Because I wanted needed the CORS option only for local development, nothing worked here for me.
The solution that I implemented, backend-side :
const io = require("socket.io")(server, {
path: '/api/socket.io',
});
if (process.env.NODE_ENV === 'development') {
io.engine.on('initial_headers', (headers, req) => {
headers['Access-Control-Allow-Origin'] = 'http://localhost:3000';
headers['Access-Control-Allow-Credentials'] = true;
});
io.engine.on('headers', (headers, req) => {
headers['Access-Control-Allow-Origin'] = 'http://localhost:3000';
headers['Access-Control-Allow-Credentials'] = true;
});
}
I was also struggling with this issue until i saw Documentation says: "You can't set 'withCredentials' to true with origin: *, you need to use a specific origin:". So my code looks like this, hope is useful:
WEB CLIENT
const { io } = require("socket.io-client");
const socket = io("localhost:3000", {
extraHeaders: {
"my-custom-header": "abcd"
}
});
SERVER
var express = require('express');
const app = express();
const http = require('http');
const httpServer = http.createServer(app);
const io = require("socket.io")(httpServer, {
cors: {
origin: '*',
methods: ["GET", "POST"],
}
});
Take a look at this:
Complete Example
Server:
let exp = require('express');
let app = exp();
//UPDATE: this is seems to be deprecated
//let io = require('socket.io').listen(app.listen(9009));
//New Syntax:
const io = require('socket.io')(app.listen(9009));
app.all('/', function (request, response, next) {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
Client:
<!--LOAD THIS SCRIPT FROM SOMEWHERE-->
<script src="http://127.0.0.1:9009/socket.io/socket.io.js"></script>
<script>
var socket = io("127.0.0.1:9009/", {
"force new connection": true,
"reconnectionAttempts": "Infinity",
"timeout": 10001,
"transports": ["websocket"]
}
);
</script>
I remember this from the combination of stackoverflow answers many days ago; but I could not find the main links to mention them
Here is the solution from the official documentation:
Since Socket.IO v3, you need to explicitly enable Cross-Origin Resource Sharing (CORS).
const io = require("socket.io")(httpServer, {cors: {
origin: "https://example.com", // or "*"
methods: ["GET", "POST"]}});
The combination that works for me is:
socketio = require('socket.io')(http, {
origins: process.env.WEB_URL, // http(s)://...
cors: {
origin: process.env.WEB_URL,
credentials: true
}
}).listen(process.env.SOCKET_PORT) // 8899
app.set('socketio', socketio)
Use following on the server side:
const express = require("express");
const cors = require("cors");
const http = require("http");
const app = express();
app.use(express.json());
app.use(cors());
const server = http.createServer(app);
const socket = require("socket.io")(server, {
cors: {
origin: "*",
methods: ["GET", "POST"],
},
});
socket.on("connection", (socket) => {
console.log("socket connection : ", socket.id);
});
server.listen(3001, () => {
console.log("server has started!");
});
i simply updated the version of socket.io from 2.x.x to 4.1.2 for backend and did the same ie. updated the version of socket.io-client at frontend from 2.x.x to 4.1.2 ....And it worked
So, basically in v2, the Socket.IO server automatically added the necessary headers to allow Cross-Origin Resource Sharing (CORS) therefor there was no problem for connection between client and server. But this behavior, while convenient, was not great in terms of security, because it meant that all domains were allowed to reach your Socket.IO server.
In v3 and above versions the CORS is disabled by default. Therefor you need to explicitly enable them on your server side script.
Example of my code:
In v2 of socket.io the server script looked like :
const io = require('socket.io')(8000);
But in v3 and above versions this code becomes to :
const io = require('socket.io')(8000, {
cors: {
origin: ['http://localhost:5500'],
},
});
// Remember by setting cors you allow you client to communicate with the socket server
// In this case 8000 is my port on which my socket connection is running and 5500 is my port where my client files are hosted.
// Socket connection runs on a different port and your client files on different
// Also you need to install socket.io-client where you have installed your socket.io modules
For more clarification I'm adding my files
This is my HTML File :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="http://localhost:8000/socket.io/socket.io.js"" content="text/javascript"></script>
<script src="javascript/client.js"></script>
<link rel="stylesheet" href="css/style.css">
<title>Chat App</title>
</head>
<body>
</body>
</html>
Here is my javascript/client.js
const socket = io('http://localhost:8000/');
And this is server/server.js
const io = require('socket.io')(8000, {
cors: {
origin: ['http://localhost:5500'],
},
});
io.on('connection', socket =>{
console.log(socket.id);
});
// If you still can't get it more detailed information can be seen on https://socket.io/docs/v4/migrating-from-2-x-to-3-0/#CORS-handling
// Also a video from which i got this solution https://youtu.be/ZKEqqIO7n-k
I had the same problem and any solution worked for me.
The cause was I am using allowRequest to accept or reject the connection using a token I pass in a query parameter.
I have a typo in the query parameter name in the client side, so the connection was always rejected, but the browser complained about cors...
As soon as I fixed the typo, it started working as expected, and I don't need to use anything extra, the global express cors settings is enough.
So, if anything is working for you, and you are using allowRequest, check that this function is working properly, because the errors it throws shows up as cors errors in the browser. Unless you add there the cors headers manually when you want to reject the connection, I guess.
Using same version for both socket.io and socket.io-client fixed my issue.
Sometimes this issue is faced when the node server stoped.
So, check if your node server working ok.
Then you can use
io.set('origins', 'http://yourdomain.com:PORT_NUMBER');
I used version 2.4.0 of socket.io in easyRTC and used the following code in server_ssl.js which worked for me
io = require("socket.io")(webServer, {
handlePreflightRequest: (req, res) => {
res.writeHead(200, {
"Access-Control-Allow-Origin": req.headers.origin,
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Referer, User-Agent, Host, Authorization",
"Access-Control-Allow-Credentials": true,
"Access-Control-Max-Age":86400
});
res.end();
}
});
If you get socket.io app working on Chrome, Safari and other browsers but you still encounter CORS issues in Firefox, and you are using a self-signed certificate, then the problem is that Firefox does not accept self-signed certificates by default, and you have to add an exception by going to Firefox's Preferences > Certificates > View Certificates > Add Exception.
If you don't do this, then Firefox shows the error you posted which is misleading, but deep within its Developer Tools, you will find this error: MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT. This indicates that Firefox is not accepting the certificate at all because it is self-signed.
const options = {
cors: {
origin:
String(process.env.ORIGINS_STRING) === "ALL"
? true
: String(process.env.ORIGINS_STRING).split(","),
methods: ["GET", "PUT", "POST", "DELETE"],
allowedHeaders: [
"Access-Control-Allow-Headers",
"X-Requested-With",
"X-Access-Token",
"Content-Type",
"Host",
"Accept",
"Connection",
"Cache-Control",
],
credentials: true,
optionsSuccessStatus: 200,
},
};
in .env file :
ORIGINS_STRING=ALL
or
ORIGINS_STRING=http://localhost:8080,http://localhost:8081
I was working with socket.io: 4.2.x, node: 14.17.x & #hapi/hapi: 20.1.x.
After trying multiple ways as mentioned in other answers, I found that the only working solutions for these version is:
const io = require('socket.io')(server.listener, { cors: { origin: '*' } });
Please make sure you have { cors: { origin: '*' } } in the options object.
I am using SocketIO with implicit http server and I am using v4.4 of socket io, I had to do it like this in the server:
const { Server } = require("socket.io");
const io = new Server(PORT, {})
io.engine.on("headers", (headers, req) => {
headers["Access-Control-Allow-Origin"] = "http://yourdomain.com"
headers["Access-Control-Allow-Headers"] = "origin, x-requested-with, content-type"
headers["Access-Control-Allow-Methodsn"] = "PUT, GET, POST, DELETE, OPTIONS"
})
const io = require('socket.io')(server, {
cors: {
origin: ['https://example.com','http://example.com','ip-address'],
}
});
Dont use origin: '*' it is a big security mistake!
origin can be used like an array with diffrent entry types:
URI with protocols like http/https
IP Address
Environment variables like process.env.WEB_URL
For testing a JavaScript / html5 application, I created a local WebSocketServer with node.js and ws package. I want to use secure websockets (wss) with SSL/TLS.
Key and certificate were create for testing purposes by OpenSSL locally (self signed certificate).
The client just tries to use the native WebSocket object to connect to the (local) https Server:
var ws = new Websocket('wss://localhost:8080');
The Problem is, no browser (Firefox, Chrome, Edge) can connect to the server and they all give me different error messages.
Firefox:
Firefox can not connect to the server at wss: // localhost: 8080 /.
Chrome:
ws_client.js:7 WebSocket connection to 'wss://localhost:8080/' failed:
Error in connection establishment: net::ERR_CERT_AUTHORITY_INVALID
Edge:
SCRIPT12017: SCRIPT12017: WebSocket Error: SECURITY_ERR, Cross zone
connection not allowed
I created the certificate and key in OpenSSL (light, newest version) like this:
openssl req -new -x509 -nodes -out server.crt -keyout server.key
(source)
I checked almost every question about this (and similar) topics, e.g. this question, but none of them could provide a solution.
Please do not mark this question as a duplicate, because all similar questions contain slightly different problems!
Server Code:
var fs = require('file-system');
var pkey = fs.readFileSync('server.key', 'utf8');
var crt = fs.readFileSync('server.crt', 'utf8');
var credentials = { key: pkey, cert: crt };
var https = require('https');
var httpsServer = https.createServer(credentials);
httpsServer.listen(8080);
var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({
server: httpsServer
});
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
ws.send('reply from server : ' + message)
});
});
I tried another code as server, but same errors occur:
const WebSocketServer = require('ws').Server;
var fs = require('file-system');
var ws_cfg = {
ssl: true,
port: 8080,
ssl_key: 'server.key',
ssl_cert: 'server.crt'
};
var processRequest = function(req, res) {
console.log('Request received');
};
var httpServ = require('https');
var app = httpServ.createServer({
key: fs.readFileSync(ws_cfg.ssl_key, 'utf8', (error) => {
console.log('Error reading file');
}),
cert: fs.readFileSync(ws_cfg.ssl_cert, 'utf8', (error) => {
console.log('Error reading file');
})
}, processRequest).listen(ws_cfg.port, function(){
console.log('Server running');
});
var wss = new WebSocketServer( {server: app, port: 8080, host: 'localhost', domain: 'localhost'} );
wss.on('connection', function (ws) {
console.log('Connected to a client');
ws.on('message', function (message) {
console.log('MSG received: ' + message);
});
});
There's one more thing. Always, if I add a console.log(wss); to the server Code, the output looks something like this:
WebSocketServer {
domain: null,
...some more stuff...
...cert key etc....
host: null,
path: null,
port: null } }
host, domain and port is set to null. I tried everything to set it to localhost:8080, but nothing worked out. I think this could be the source of all Problems, but can't find a way. If anyone knows an answer to this question, I would highly appreciate it.
(Using the insecure 'ws' protocol ('ws://localhost:8080') in order to connect to local node.js http server works, but I want to test the app as realistic as possible and use a secure Connection.)
-- This is not an answer, just my workaround --
For anyone having the same problems, here is what I did:
Server Code should be:
const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');
const server = new https.createServer({
cert: fs.readFileSync('localcert.cert'), //what ever you're files are called
key: fs.readFileSync('localkey.key')
});
const wss = new WebSocket.Server({ server }); // !
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('MSG received: %s', message);
});
ws.send('Hi to client');
});
server.listen(8080);
Only working in Google Chrome for now, can still not connect in Firefox.
enter chrome://flags/#allow-insecure-localhost in Google Chrome and enable.
Try to add the self-signed certificate or the generated CA to be trusted on the system that you are using.