为什么HTTP请求头没有Origin? 从前端向后端发出任何请求后,我面临 CORS 冲突。
我有两个项目。两者都位于一台服务器上,具有一个公共 IP,但域不同。
前端(React.js)
IP - 84.201.143.32
域名 - https://place.nomoredomains.xyz
后端(Node.js、express、mongoDB)
IP - 84.201.143.32
域名 - https://api.place.nomoredomains.xyz
前端(React.js)
IP - 84.201.143.32
域名 - https://ypdiploma.nomoreparties.co/
后端(Node.js、express、mongoDB)
IP - 84.201.143.32
域名 - https://api.ypdiploma.nomoreparties.co
这两个项目都通过 Nginx 运行。
这是站点可用/默认配置:
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
}
项目 #1 工作正常,并发送所有带有 Origin 标头的请求,但项目 #2 不发送任何 Origin 标头。因此,我遇到了项目 #2 的 CORS 冲突。
我想你可能还需要 corsHandler 中间件代码:
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 };
这是我的应用程序:
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);
最后从前端部分获取:
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;
我尝试使用 Referer 标头实现 CORS 检查,但没有成功。我怀疑问题出在 NGINX 的默认配置中,但不幸的是我找不到哪里......
您的逻辑会添加
Access-Control-Allow-Credentials
标头两次,以响应从您允许的来源之一发出的 OPTIONS
请求:
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();
}
您应该抵制通过手动设置 CORS 响应标头来“手动”实现 CORS 的诱惑;除非您非常熟悉 CORS 协议,否则这样做很容易出错。 Express.js 有一个 CORS 中间件;学习(安全地)配置它并使用它。
另请注意,将
http://ypdiploma.nomoreparties.co/
和 https://ypdiploma.nomoreparties.co/
列为允许的来源是不好的:这两个值无效 Web 来源,因为 来源不包含路径。