Issue with 'Access-Control-Allow-Origin' in my Node.js application

I’ve been stuck on this problem for a few days now and I can’t seem to find a solution. Thank you in advance for your help.

I’ve configured CORS permissions in my index file as follows:

const corsOptions = {
  origin: process.env.CORS_ORIGIN,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization', 'Pragma', 'Cache-Control'],
  credentials: true
};

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', process.env.CORS_ORIGIN);
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Pragma, Cache-Control');
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  res.setHeader('Pragma', 'no-cache');
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
  console.log('CORS headers:', res.getHeaders());

  next();
});

app.options('*', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', process.env.CORS_ORIGIN);
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Pragma, Cache-Control');
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  res.sendStatus(204);
});

app.use((req, res, next) => {
  res.on('finish', () => {
    const headers = res.getHeaders();
    console.log('CORS headers:', headers);

    console.log('Access-Control-Allow-Origin:', headers['access-control-allow-origin']);
    console.log('Access-Control-Allow-Methods:', headers['access-control-allow-methods']);
    console.log('Access-Control-Allow-Headers:', headers['access-control-allow-headers']);
    console.log('Pragma:', headers['pragma']);
    console.log('Cache-Control:', headers['cache-control']);
  });

  next();
});

(Note: corsOptions is not currently being used, as I’ve been doing a lot of testing.)

Despite this, I’m still getting the following error:

Access to XMLHttpRequest at ‘https://api.myapp.app/user/me’ from origin ‘https://myapp.app’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

I’ve also configured my two Nginx configurations to ensure they do not handle CORS:

api.myapp.app:

server {
    listen 80;
    server_name api.myapp.app;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name api.myapp.app;

    ssl_certificate /etc/letsencrypt/live/api.myapp.app/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.myapp.app/privkey.pem;
    ssl_dhparam /etc/nginx/ssl-dhparams.pem;

    location /api/ {
        proxy_pass http://localhost:3000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

myapp.app:

server {
    listen 80;
    server_name myapp.app;

    # Redirect to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name myapp.app;

    root /home/myapp/frontend/preview/browser;
    index index.html;

    ssl_certificate /etc/letsencrypt/live/myapp.app/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myapp.app/privkey.pem;
    ssl_dhparam /etc/nginx/ssl-dhparams.pem;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://localhost:3000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

I really can’t find any solution. I’ve tried several things: re-enabling headers via Nginx resulted in a problem with multiple values (back + nginx). I also disabled CORS management via my backend, but then there was an issue with the pragma header not being transmitted…

By the way, what is the difference between handling CORS in Nginx versus in Node.js?

Thank you very much for your help, I’ve been struggling with this for several days…

It looks like you’re facing a common issue with handling CORS in a Node.js application behind an Nginx proxy. The CORS error you’re seeing is usually because the Access-Control-Allow-Origin header is not being correctly set or passed in the response to the client. Let’s go through a few potential solutions and clarify some concepts:

  1. Handling CORS in Node.js and Nginx:

    Node.js: Handling CORS directly in your Node.js application is typically preferred because it gives you more granular control over which origins, methods, and headers are allowed. It also allows you to customize responses based on the request, such as varying the Access-Control-Allow-Origin dynamically.
    Nginx: Handling CORS in Nginx can be simpler and offloads some work from your application, but it offers less flexibility. It’s generally better to handle it at the application level (in Node.js) if you need more dynamic behavior.

  2. Common Issues and Solutions:

    Check Nginx Configuration: You mentioned disabling CORS handling in Nginx, which is good. However, ensure that no residual CORS-related headers are being set in Nginx configurations that might interfere with the headers set in your Node.js application.

    CORS Headers in Node.js: Based on your Node.js code, you’re setting the CORS headers in middleware. Make sure that this middleware is executed before any routes or other middleware that might end the request/response cycle prematurely. Middleware ordering is critical.

    Dynamic Access-Control-Allow-Origin: You’re using process.env.CORS_ORIGIN to set the Access-Control-Allow-Origin. Ensure that this environment variable is correctly set and matches the origin of the request (https://myapp.app). If your environment variable is not set correctly, the header won’t be applied as expected. You can try hardcoding the origin temporarily to see if that resolves the issue:

    js

    res.setHeader(‘Access-Control-Allow-Origin’, ‘https://myapp.app’);

    This test will help you determine if the issue is with the environment variable.

    Preflight Requests: The error message you provided indicates a problem with preflight requests. Ensure that your OPTIONS route (for handling preflight) correctly sends the CORS headers and a 204 No Content response. In your code, you seem to be doing this, which is good. However, double-check if this route is being reached correctly.

    Match Paths: Ensure that your CORS configuration and preflight handling are being applied to the correct paths. If you only have /api/ paths in Nginx, make sure the CORS middleware in Node.js applies to these paths.

  3. Troubleshooting Steps:

    Debug Middleware: Add logging inside your CORS middleware to see the values of process.env.CORS_ORIGIN and the headers being set. This can help identify if there’s a mismatch.

    js

console.log(‘Setting CORS headers for:’, process.env.CORS_ORIGIN);

Inspect Network Requests: Use browser developer tools to inspect the network requests, particularly the preflight OPTIONS request. Check the response headers to see what’s being returned. You should see:

Access-Control-Allow-Origin: https://myapp.app
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Pragma, Cache-Control
Access-Control-Allow-Credentials: true

Try the cors Package: If the problem persists, consider using the cors middleware package in your Node.js application. It’s specifically designed for handling CORS:

js

const cors = require('cors');
const corsOptions = {
  origin: process.env.CORS_ORIGIN, // or hardcode for testing: 'https://myapp.app'
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization', 'Pragma', 'Cache-Control'],
  credentials: true
};
app.use(cors(corsOptions));
  1. Difference Between Handling CORS in Nginx vs. Node.js:

    Nginx: It’s generally more straightforward and offloads some processing, but it’s less flexible. Use Nginx if your CORS policies are static and do not require dynamic origins or headers based on the request.
    Node.js: Allows dynamic handling and more control over the request/response. Use this if you need to vary CORS behavior based on the origin, request path, or other factors.

Conclusion

Double-check your process.env.CORS_ORIGIN, ensure your middleware order is correct, and consider using the cors package to simplify handling. Logging and inspecting the actual headers being sent in responses will be crucial in diagnosing the issue.