skip to Main Content

Why do HTTP request headers have no Origin?
I’m facing a CORS conflict after any request from my frontend to my backend.

I have two projects. Both located at one server and with one public IP but different domains.

Project #1:

Frontend (React.js)
IP – 84.201.143.32
Domain – https://place.nomoredomains.xyz
Backend (Node.js, express, mongoDB)
IP – 84.201.143.32
Domain – https://api.place.nomoredomains.xyz

Project #2:

Frontend (React.js)
IP – 84.201.143.32
Domain – https://ypdiploma.nomoreparties.co/
Backend (Node.js, express, mongoDB)
IP – 84.201.143.32
Domain – https://api.ypdiploma.nomoreparties.co

Both projects works through Nginx.
Here is sites-available/default config:

server {

      server_name api.place.nomoredomains.xyz;

   location / {
                proxy_pass http://localhost:3000;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header Host $host;
                proxy_cache_bypass $http_upgrade;
      }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/place.nomoredomains.xyz/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/place.nomoredomains.xyz/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {

      server_name place.nomoredomains.xyz;

      root /home/lexev/react-mesto-api-full-gha/frontend/build;

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

    listen 443 ssl; # managed by Certbot
     ssl_certificate /etc/letsencrypt/live/place.nomoredomains.xyz/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/place.nomoredomains.xyz/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}


server {

      server_name api.ypdiploma.nomoreparties.co;

      location / {
                proxy_pass http://localhost:3001;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header Host $host;
                proxy_cache_bypass $http_upgrade;
      }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/place.nomoredomains.xyz/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/place.nomoredomains.xyz/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {

      server_name ypdiploma.nomoreparties.co;

      root /home/lexev/ypdiploma/movies-explorer-frontend/build;

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

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/place.nomoredomains.xyz/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/place.nomoredomains.xyz/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}


server {
    if ($host = place.nomoredomains.xyz) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


      listen 80;

      server_name place.nomoredomains.xyz;
    return 404; # managed by Certbot


}server {
    if ($host = api.place.nomoredomains.xyz) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


      listen 80;

      server_name api.place.nomoredomains.xyz;
    return 404; # managed by Certbot


}

server {
    if ($host = ypdiploma.nomoreparties.co) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


      listen 80;

      server_name ypdiploma.nomoreparties.co;
    return 404; # managed by Certbot


}


server {
    if ($host = api.ypdiploma.nomoreparties.co) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


      listen 80;

      server_name api.ypdiploma.nomoreparties.co;
    return 404; # managed by Certbot


}

Project #1 works properly and sending all request with Origin Header, but Project #2 do not send any Origin Header. Due to this i getting CORS conflict for Project #2.

Project #1 HTTP request: Network Tab from Chrome

Project #2 HTTP request: Network Tab from Chrome

CORS Error:
screenshot

I think you also may require corsHandler middleware code:

const allowedCors = [
  'https://ypdiploma.nomoreparties.co',
  'http://ypdiploma.nomoreparties.co',
  'https://ypdiploma.nomoreparties.co/',
  'http://ypdiploma.nomoreparties.co/',
  'http://localhost:3000',
];

const corsHandler = (req, res, next) => {
  const { origin } = req.headers;
  const { method } = req;

  const DEFAULT_ALLOWED_METHODS = 'GET,HEAD,PUT,PATCH,POST,DELETE';
  const requestHeaders = req.headers['access-control-request-headers'];

  if (allowedCors.includes(origin)) {
    res.header('Access-Control-Allow-Origin', origin);
    res.header('Access-Control-Allow-Credentials', true);
  }

  if (method === 'OPTIONS') {
    res.header('Access-Control-Allow-Methods', DEFAULT_ALLOWED_METHODS);
    res.header('Access-Control-Allow-Headers', requestHeaders);
    res.header('Access-Control-Allow-Credentials', true);
    return res.end();
  }

  return next();
};

module.exports = { corsHandler };

This is my app:

require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cookieParser = require('cookie-parser');
const { errors } = require('celebrate');
const helmet = require('helmet');
const { requestLogger, errorLogger } = require('./middlewares/logger');
const { corsHandler } = require('./middlewares/cors');
const { limiter } = require('./middlewares/limiter');

const errorHandler = require('./middlewares/errorHandler');
const { MONGOOSE_DB } = require('./constants/constants');

const { PORT = 3001 } = process.env;

const app = express();
mongoose.connect(MONGOOSE_DB);

app.use(helmet());
app.use(corsHandler);
app.use(express.json());
app.use(cookieParser());
app.use(requestLogger);

app.use('/', limiter, require('./routes/index'));

app.use(errorLogger);

app.use(errors());
app.use(errorHandler);

app.listen(PORT);

And finally fetch from Frontend part:

class MainApi {
  constructor() {
    this._baseUrl = 'https://api.ypdiploma.nomoreparties.co'
    this._headers = {
      'Content-Type': 'application/json',
    };
  }

  _getResponseData(res) {
    if (res.ok) {
      return res.json();
    } else {
      return Promise.reject(res);
    }
  }

  getUserinfo() {
    return fetch(this._baseUrl + '/users/me', {
      headers: this._headers,
      credentials: 'include',
    }).then((res) => this._getResponseData(res));
  }
}

const mainApi = new MainApi();

export default mainApi;

I tried to implement a CORS check with the Referer header, but it didn’t works. I suspect that the problem is somewhere in NGINX’s default config, but unfortunately I can’t find where…

2

Answers


  1. Chosen as BEST ANSWER

    How mentioned @jub0bs, the rason was in handmade corsHandler.
    After using Express's CORS middleware with correct settings everything is work!

    Сonfigured cors middleware:

    const cors = require('cors');
    
    const corsHandler = cors({
      origin: [
        'https://ypdiploma.nomoreparties.co',
        'http://ypdiploma.nomoreparties.co',
        'http://localhost:3000',
      ],
      optionsSuccessStatus: 200,
      credentials: true,
    });
    
    module.exports = { corsHandler };
    

    app:

    require('dotenv').config();
    const express = require('express');
    const mongoose = require('mongoose');
    const cookieParser = require('cookie-parser');
    const { errors } = require('celebrate');
    const helmet = require('helmet');
    const { corsHandler } = require('./middlewares/cors');
    const { requestLogger, errorLogger } = require('./middlewares/logger');
    const { limiter } = require('./middlewares/limiter');
    
    const errorHandler = require('./middlewares/errorHandler');
    const { MONGOOSE_DB } = require('./constants/constants');
    
    const { PORT = 3001 } = process.env;
    
    const app = express();
    mongoose.connect(MONGOOSE_DB);
    
    app.use(corsHandler);
    app.use(helmet());
    app.use(express.json());
    app.use(cookieParser());
    app.use(requestLogger);
    
    app.use('/', limiter, require('./routes/index'));
    
    app.use(errorLogger);
    
    app.use(errors());
    app.use(errorHandler);
    
    app.listen(PORT);
    

  2. Your logic adds the Access-Control-Allow-Credentials header twice in responses to OPTIONS requests issued from one of your allowed origins:

      if (allowedCors.includes(origin)) {
        res.header('Access-Control-Allow-Origin', origin);
        res.header('Access-Control-Allow-Credentials', true); // added once
      }
    
      if (method === 'OPTIONS') {
        res.header('Access-Control-Allow-Methods', DEFAULT_ALLOWED_METHODS);
        res.header('Access-Control-Allow-Headers', requestHeaders);
        res.header('Access-Control-Allow-Credentials', true); // added twice
        return res.end();
      }
    

    You should resist the temptation to implement CORS "by hand", by manually setting CORS response headers; doing so, unless you’re intimately familiar with the CORS protocol, is error-prone. Express.js has a CORS middleware; learn to configure it (securely) and use that instead.

    Also, note that listing http://ypdiploma.nomoreparties.co/ and https://ypdiploma.nomoreparties.co/ as allowed origins is no good: those two values are not valid Web origins, because origins don’t contain a path.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search