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:
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
How mentioned @jub0bs, the rason was in handmade corsHandler.
After using Express's CORS middleware with correct settings everything is work!
Сonfigured cors middleware:
app:
Your logic adds the
Access-Control-Allow-Credentials
header twice in responses toOPTIONS
requests issued from one of your allowed origins: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/
andhttps://ypdiploma.nomoreparties.co/
as allowed origins is no good: those two values are not valid Web origins, because origins don’t contain a path.