CORS Issue with Cloudflare: Empty Response Headers Despite Worker and Rules Setup

I am experiencing persistent CORS issues when trying to access my Express server from a published client using Cloudflare for DNS management. Despite various attempts, the Access-Control-Allow-Origin header is not being set correctly in the response, leading to CORS errors.

Issue Description

When attempting to make a request from my client application (https://app.rorofume.boats) to my server (https://server.rorofume.boats), I encounter the following CORS error:

Access to XMLHttpRequest at 'https://server.rorofume.boats/users?_id=TsJYVYrudlOB0c6k0A3qosisLId2' from origin 'https://app.rorofume.boats' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

What Works

  • Accessing the server from a local client (running on localhost) works without any issues.
  • Direct requests to the server using curl return the expected response with the correct CORS headers:
curl -v -k -H "Origin: https://app.rorofume.boats" "https://server.rorofume.boats/users?_id=TsJYVYrudlOB0c6k0A3qosisLId2"

Steps Taken

  1. Cloudflare Workers:
    I created a Cloudflare Worker to handle CORS headers:
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const origin = request.headers.get('Origin')
  const allowedOrigins = [
    'http://localhost:5173',
    'http://localhost:5174',
    'http://localhost:5175',
    'https://rorofume.boats',
    'https://www.rorofume.boats',
    'https://app.rorofume.boats'
  ]

  const corsHeaders = {
    'Access-Control-Allow-Origin': allowedOrigins.includes(origin) ? origin : '',
    'Access-Control-Allow-Credentials': 'true',
    'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept',
    'Access-Control-Allow-Methods': 'GET,HEAD,PUT,PATCH,POST,DELETE'
  }

  if (request.method === 'OPTIONS') {
    // Handle CORS preflight request
    return new Response(null, {
      headers: corsHeaders
    })
  }

  // Handle actual request
  const response = await fetch(request)
  const newHeaders = new Headers(response.headers)
  Object.keys(corsHeaders).forEach(key => {
    newHeaders.set(key, corsHeaders[key])
  })

  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers: newHeaders
  })
}
  1. Modify Response Header Rule:
    I set up a rule in Cloudflare to modify the response headers, specifically to add the Access-Control-Allow-Origin header based on the following answers.

Current Observations

  • Despite these configurations, the response headers remain empty when inspecting the network request in the browser’s Developer Tools.
  • The issue does not occur when accessing the server from a local client, indicating that the server setup itself is correct.

Hosting Information

  • The server is hosted on Railway.
  • The client application is hosted on Cloudflare Pages.
  • Railway provides a default app URL (e.g., something.up.railway.app).

Additional Findings

  1. Both client & server run locally - everything works.
  2. Client runs locally and accesses the server, either using the default Railway URL or the custom URL (DNS managed via Cloudflare) - works.
  3. Published client (Cloudflare Pages) accessing the server via the default Railway URL - works.
  4. Published client (Cloudflare Pages) trying to access the server via the custom URL (DNS managed by Cloudflare) - doesn’t work.
    This narrows the issue down to the communication between the Cloudflare-hosted client and the server with the domain managed by Cloudflare.

Express Server Configuration

Here’s the relevant part of my Express server configuration:

import express, { Request, Response, NextFunction } from 'express';
import cors from 'cors';
import http from 'http';

const app = express();
const server = http.createServer(app);

const corsOptions = {
  origin: [
    'http://localhost:5173',
    'http://localhost:5174',
    'http://localhost:5175',
    'https://rorofume.boats',
    'https://www.rorofume.boats',
    'https://app.rorofume.boats'
  ],
  credentials: true,
  allowedHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept'],
  methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
};

app.use(cors(corsOptions));
app.use(express.json());

// Logging middleware
app.use((req: Request, res: Response, next: NextFunction) => {
  console.log('Request received:', req.method, req.url);
  console.log('Origin:', req.headers.origin);
  next();
});

app.use((req: Request, res: Response, next: NextFunction) => {
  console.log('CORS headers before next:', res.get('Access-Control-Allow-Origin'));
  next();
  console.log('CORS headers after next:', res.get('Access-Control-Allow-Origin'));
});

// Example route to test CORS
app.get('/users', (req: Request, res: Response) => {
  res.json({ message: 'CORS is working!' });
});

// Error handling middleware
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
  console.error('An error occurred:', err);
  res.status(500).json({ error: 'Internal Server Error' });
});

// Server listening setup
const PORT = process.env.PORT || 3000;

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

Client Configuration

Here’s the client-side code making the request:

const baseUserPath = `${import.meta.env.VITE_PUBLIC_SERVER_BASE_URL}/users`;

useEffect(() => {
  if (!auth) return;
  const authStateListener = onAuthStateChanged(auth, (newAuthUserState) => {
    if (newAuthUserState) {
      setAuthUser(newAuthUserState);
      (async () => {
        try {
          const response = await axios.get(baseUserPath, {
            params: {
              _id: newAuthUserState.uid,
            },
            withCredentials: true,
          });

          setUser(response.data);
        } catch (err: unknown) {
          if (err instanceof Error) {
            throw new Error(err.message);
          }
          throw err;
        } finally {
          setLoadingInitial(false);
        }
      })();
    } else {
      setAuthUser(undefined);
      setUser(undefined);
      setLoadingInitial(false);
    }
  });

  return authStateListener;
}, [auth]);

Request for Help

I’m looking for insights on why the CORS headers are not being set correctly when accessed from the published client (https://app.rorofume.boats). Despite trying Cloudflare Workers and modifying response headers, the issue persists with empty response headers in the network tab. The problem appears to be specific to the communication between the Cloudflare-hosted client and the server with the domain managed by Cloudflare. Any suggestions on what might be causing this issue or further steps to diagnose and resolve it would be greatly appreciated.

Thank you!

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.