0

I'm slowly losing my mind over a very stupid issue I'm having.

I have a socket.io/express app uploaded to Digital Ocean as a Docker setup.

To allow https, I am using caddy as part of my Docker setup to allow for automatic https.

I've been trying to connect to this setup via my domain and from my local React app that lives on localhost:3000. But I am constantly getting the following error:

Access to XMLHttpRequest at 'https://mediaserver.domain.dev/socket.io/?EIO=3&transport=polling&t=N5BXNK2' from origin 'http://localhost:3000' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

I know there have been a lot of SO questions about this before and believe me when I say I tried almost all of them.

  • I tried changing the options of the cors middleware
  • I tried adding my own middleware and setting headers specifically
  • I tried using localhost:3000 as origin
  • ...

But nothing seems to work. I have currently no idea what I can still do to fix this.

So any help would be welcome.

My docker-compose file looks as follows:

version: '3.6'
services:
  media-server:
    build:
      dockerfile: Dockerfile
      context: ./
    ports:
    - "8080:5000"
    expose: 
      - "5000"
  caddy:
    image: abiosoft/caddy:0.11.0
    depends_on:
      - "media-server"
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /root/Caddyfile:/etc/Caddyfile
      - /root/.caddy:/root/.caddy

My Caddyfile is as follows:

https://mediaserver.domain.dev {
  proxy / http://media-server:8080 {
    websocket
    transparent
  }
  cors
}

And my server setup looks as follows:

import cors from 'cors';
import express from 'express';
import socket from 'socket.io';

import { intialiseWebSocketConnection } from './socketio';

const app = express();

app.use(cors());

const server = app.listen(5000, function () {
  console.log('Server is connectedd on *:5000');
});

const io = socket.listen(server);

intialiseWebSocketConnection(io);
sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
  • did you try Access-Control-Allow-Origin: http://localhost:3000 with http:// in the beginning? not just localhost:3000 – i.brod Apr 05 '20 at 18:21
  • @i.brod I did, sorry, should have been more specific. I am not sure if I should change something in the express setup or in caddy really. – Rein Van Imschoot Apr 05 '20 at 18:36

2 Answers2

3

You are attempting to make a cross-origin request with the credentials flag set and the Access-Control-Allow-Origin set to any (*). This is not allowed for security reasons. There are two ways to solve the problem. If you don't need to send credentials make sure the credentials flag is false. That is, if you are using an XMLHttpRequest make sure withCredentials is not true (it is false by default). If you are using the Fetch API make sure Request.credentials is set to "omit".

If you do need to send credentials for some reason, you have to set the Access-Control-Allow-Origin in your server's response to the origin from where you are sending requests to the server and not to any (*). To figure out what your origin is just check to what the Origin header is set to in the requests you send to the server.

By default cors() sets the Access-Control-Allow-Origin to *. Try changing it to:

cors({
    origin: "http://localhost:3000",
    credentials: true
});

Also note Chrome does not support localhost as an origin. To get around that you can start Chrome with the --disable-web-security flag during development.

domondo
  • 926
  • 7
  • 9
  • Thanks for the reply! In my React app I am using the socket.io client, so I am not sure what it uses under the hood for the XMLHttpRequest nor do I know where to change it. I don't think I need credentials for now? I'm just trying to make it work for now. – Rein Van Imschoot Apr 05 '20 at 18:35
  • @ReinVanImschoot Sorry, didn't read your question carefully enough. You can't change the credentials mode in socket.io (I believe). You will have to change the Access-Control-Allow-Origin header. And importantly Chrome does not support localhost as origin, be careful with that. I edited my answer. Hope something is useful. – domondo Apr 05 '20 at 19:02
2

There's a simple way to bypass CORS errors for socket.io connections. By default socket.io starts each connection with several regular http calls. If things go well, it converts to a webSocket transport (and runs socket.io over the webSocket transport). Those initial http calls are subject to CORs restrictions.

But, if you tell socket.io to just start out with the webSocket transport right away, then you are not subject to CORs restrictions. You can do that in the client by doing this:

const socket = io({transports: ['websocket']});

Even though a webSocket connection always starts with one http request, that particular http request (with the appropriate upgrade header set is not subject to CORs restrictions.

The only downside to this (that I'm aware of) is that your code would not run in a browser that does not support webSockets. That would be a really, really old browser. Even IE10 (released in 2012) supports webSockets and all modern browsers have supported them since at least 2013. I'm actually not quite sure why socket.io still has their http polling as the default as it's far less efficient on the network to start out every connection. Anyway, you can easily bypass it with io({transports: ['websocket']});.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thank you very much! I came to that realisation as well. It's weird that they still use polling when websockets is integrated. I might just switch from socket.io to something more lower level as the websocket library. Thank you very much for the response! – Rein Van Imschoot Apr 06 '20 at 06:52
  • @ReinVanImschoot - Socket.io has a lot of advantages that I think are worth it. See here and decide for your own needs: [Moving from socket.io to raw webSockets](https://stackoverflow.com/questions/38546496/moving-from-socket-io-to-raw-websockets/38546537#38546537). – jfriend00 Apr 06 '20 at 13:25
  • For now I feel it makes everything just more difficult, since I also need to account for these urls "wss:/?mydomain.com/socket.io/?EIO=3&transport=websocket" and thus setup rules in Caddy etc. I'm not very experienced with all this so I'm not sure whether socket.io is an actual help right now. Since Im using it as a signalling server for WebRTC, I might use something like Protoo perhaps? – Rein Van Imschoot Apr 06 '20 at 15:21