How to troubleshoot a cross domain server-sent events connection?

The Problem

Few days back I was trying to troubleshoot a cross domain server-sent events (SSE) connection issue. The issue which I was facing was that the browser was not sending any preflight request which is required to validate if cross domain requests to the target API are allowed from the domain of a webpage or not.

You can open a SSE connection from JavaScript using following code -

const eventSoource = new EventSource(url)

As this endpoint was running on a different domain and was a secure endpoint, it was required to send session cookie to this endpoint for authentication/authorization which can be achieved as follows -

const eventSoource = new EventSource(url, {withCredentials: true})

Another thing which we need to understand it that a SSE endpoint must be a GET endpoint and there is no option to pass a header. As all our API endpoints required some mandatory headers for different purposes, we had to pass the mandatory information (which we were passing in the headers for other endpoints) as query parameters for this SSE endpoint.

However, this did not make any difference and the browser continued to ignore preflight request for such SSE endpoint. The reason behind it was that it was a simple request and a browser does not trigger CORS preflight request for a simple request.

Simple vs Complex request

Simple Request:

  • Definition: Simple requests are those that do not trigger a CORS preflight request.

  • Characteristics:

    • Simple requests include methods like GET, POST, or HEAD.

    • They do not include custom headers beyond a specific set of simple headers.

    • The Content-Type header is limited to certain values like text/plain, application/x-www-form-urlencoded, or multipart/form-data.

  • Example: A GET request without custom headers would be considered a simple request.

Complex Request:

  • Definition: Complex requests trigger a CORS preflight request due to specific characteristics.

  • Characteristics:

    • Use of methods other than GET, POST, or HEAD.

    • Inclusion of custom headers beyond the allowed set for simple requests.

    • Content-Type header with values other than text/plain, application/x-www-form-urlencoded, or multipart/form-data.

  • Example: A POST request with custom headers or a non-standard Content-Type would be considered a complex request.

The Solution

The trick was to send a OPTIONS request (similar to the one which is sent by the browser during the preflight request) manually and it fixed the issue. Here is sample code for same -

const BASE_URL = 'https://api.example.com'
const uri = '/events?param1=value1'

const client = axios.create({
   baseURL: BASE_URL
})

return await client.options(
   uri.split('?')[0], 
   {
      method: 'OPTIONS'
   }
)
.then(response => {
   const eventSource = new EventSource(`${BASE_URL}${uri}`, { withCredentials: true })
})
0
Subscribe to my newsletter

Read articles from Shailendra Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shailendra Singh
Shailendra Singh

I am founder of MechCloud (https://mechcloud.io), a SaaS application which helps users to design websites and build Cloud Computing infrastructure using visualization first approach. MechCloud is accepted into following startup programs - MongoDB for Startup '23 Google for Startup Cloud '23 Microsoft for Startups Founders Hub '23 AWS Activate `23 Cloudflare for Startups '24