Nginx 跨域 + 无法设置 Cookie 解决办法 + 使用域名访问

首先 F12 看 login 接口对应的网络请求有没有 ⚠️,如果有那是后端的问题,如果没有那是前端的问题

前端问题

前端没有携带 cookie 导致后端识别不到

1) 前端 axios 是否开启了 withCredentials=true

2) 在 OpenAPI 的那边配置项,设置下 withCrendential

img

后端问题

确认环境

1)确保后端跨域注解或者配置要删除,鱼皮项目一般是 config/CrosConfig.java

跨域方式只有一种就可以(一般是后端配置或者 Nginx),咱们这里就使用 Nginx 解决跨域

2)如果使用国内服务器,想要使用域名必须要备案

3)YML 配置

▼
yml
复制代码
server: servlet: session: cookie: domain: 域名或者IP

http 环境就不要使用 secure 和samesite

使用宝塔跨域

Easy 跨域配置

在宝塔前端配置文件

非常重要,一定确保「前端请求地址」和「前端运行地址」地址一致!!! 这种方式需要确保 1、后端没有配置跨域, 一般是 config/CrosConfig.java (具体内容下面的图片) 2、前端请求的地址是,前端运行的 IP 或者前端的域名 比如下面这几种情况 1)比如前端 IP 是 123.123.123.123 那么前端请求后端的地址也是 123.123.123.123 2)比如前端 IP 是 123.123.123.123:90 那么前端请求后端的地址也是 123.123.123.123:90 3)比如前端域名是 leikooo.com 那么前端请求后端请求的地址也是 leikooo.com

image.png

前端请求地址和前端运行地址一致 也就是前端 baseURL 请求 192.168.196.158:8000 具体参考自己运行地址

后端跨域配置

image.png

需要修改宝塔具体位置

image.png image.png

如果是原生的 Nginx 下面是完整的代码,如果是宝塔的话只需要复制两个 location 模块到前端配置文件即可 ,注意一定不要换顺序!

▼
text
复制代码
server { listen 80; server_name 前端 IP 比如 126.4.3.3; root 前端路径; location /api { proxy_pass http://127.0.0.1:后端端口; proxy_set_header Host $proxy_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; proxy_set_header Connection ""; } # 这个要写在下面! location / { index index.html index.htm; try_files $uri $uri/ /index.html; } }

BUG

比如,下面这种情况:

image.png

所以会导致跨域失败!

解决方法:前端的 baseUrl 修改成前端运行地址

Hard 跨域配置

1、如果之前使用的是下面这种跨域方式也要删除掉/注释! image.png

2、这种方式是前端请求一个独立于前端和后端的端口,比如 前端 80 后端 8080 ,那么前端请求 90 端口让 90 对端口进项反向代理到后端

3、上面的 easy 方式虽然简单,但是如果后端想要使用独立的子域名的情况下就不适用了


image-20240914115007224 image-20240914115143570
▼
nginx
复制代码
# 后端相关的反向代理+跨域 server { # 这个监听的端口任意都行,但是要注意前端要请求这个端口 listen 90; server_name 前端 IP 比如 126.4.3.3; location / { # 禁止非 GET|POST|HEAD|OPTIONS|PUT|PATCH|DELET 的请求 if ( $request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|PATCH|DELETE)$ ) { return 444; } set $origin $http_origin; # 重点!比如: # $origin !~ '^http?://leikooo\.com$ # $origin !~ '^http?://127.0.0.1$ if ($origin !~ '^http?://服务器IP$') { set $origin 'http://服务器IP'; } if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' "$origin" always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Content-Type, Accept, Authorization' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header Access-Control-Max-Age 1728000; add_header Content-Type 'text/plain charset=UTF-8'; add_header Content-Length 0; return 204; } if ($request_method ~ '(GET|POST|PATCH|PUT|DELETE)') { add_header Access-Control-Allow-Origin "$origin" always; add_header Access-Control-Allow-Methods 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header Access-Control-Allow-Headers 'Content-Type, Accept, Authorization' always; add_header Access-Control-Allow-Credentials true always; } # 反向代理到后端具体运行的端口 proxy_pass http://localhost:后端实际运行端口; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

请求流程图

image.png

使用域名

前端请求后端的域名,然后在 Nginx 配置跨域和反向代理,访问到真正的后端

▼
nginx
复制代码
# 后端相关的反向代理+跨域 server { # http 默认 80 端口 https 默认 443 端口 listen 80; server_name 修改成自己的子域名; # 比如 backend.leikooo.com location / { # 禁止非 GET|POST|HEAD|OPTIONS|PUT|PATCH|DELET 的请求 if ( $request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|PATCH|DELETE)$ ) { return 444; } set $origin $http_origin; # 重点!比如: # $origin !~ '^http?://leikooo\.com$ # 下面配置前端域名,比如 leikooo.cn if ($origin !~ '^http?://前端域名$') { set $origin '前端域名'; } if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' "$origin" always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Content-Type, Accept, Authorization' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header Access-Control-Max-Age 1728000; add_header Content-Type 'text/plain charset=UTF-8'; add_header Content-Length 0; return 204; } if ($request_method ~ '(GET|POST|PATCH|PUT|DELETE)') { add_header Access-Control-Allow-Origin "$origin" always; add_header Access-Control-Allow-Methods 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header Access-Control-Allow-Headers 'Content-Type, Accept, Authorization' always; add_header Access-Control-Allow-Credentials true always; } # 反向代理到后端具体运行的端口 # 如果后端开启了 https 不要忘记请求协议变成 https proxy_pass http://localhost:后端实际运行端口; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

注意:

1)前端请求 90 (上面 server 模块下 listen 的端口)而不是直接请求后端实际运行端口

image.png

2) 直接请求后端端口,那么 Nginx 就失去了存在的意义!

3)宝塔 + 服务器放行 90 端口,这个要注意!!(具体看自己写的是哪个端口)

4)完成 添加 nginx 配置 + 放行端口 正常就没什么问题了!

5)后端配置文件如果写了 samesite 或者 sercure 属性要删除掉!

image.png

6)后端写了跨域配置要 删除/注释 一般是 @CrossOrigin 或者 CrosConfig

使用原生 Nginx 跨域

经过实际测试,用 nginx 跨域就可以解决

▼
nginx
复制代码
user root; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; access_log logs/access.log; sendfile on; keepalive_timeout 65; #gzip on; # 前端配置不是重点 server { listen 80; server_name 前端 IP 比如 126.4.3.3 ; root /root/app/dist; # 访问默写前端页面 404 就是没加下面这行的原因 try_files $uri $uri/ /index.html; location / { index index.html index.htm; } } # 后端相关的反向代理+跨域 server { # 这个监听的端口任意都行,但是要注意前端要请求这个端口 listen 90; server_name 前端 IP 比如 126.4.3.3; location / { # 禁止非 GET|POST|HEAD|OPTIONS|PUT|PATCH|DELET 的请求 if ( $request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|PATCH|DELETE)$ ) { return 444; } set $origin $http_origin; # 重点!比如: # $origin !~ '^http?://leikooo\.com$ # $origin !~ '^http?://127.0.0.1$ if ($origin !~ '^http?://服务器IP$') { set $origin 'http://服务器IP'; } if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' "$origin" always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Content-Type, Accept, Authorization' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header Access-Control-Max-Age 1728000; add_header Content-Type 'text/plain charset=UTF-8'; add_header Content-Length 0; return 204; } if ($request_method ~ '(GET|POST|PATCH|PUT|DELETE)') { add_header Access-Control-Allow-Origin "$origin" always; add_header Access-Control-Allow-Methods 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header Access-Control-Allow-Headers 'Content-Type, Accept, Authorization' always; add_header Access-Control-Allow-Credentials true always; } # 反向代理到后端具体运行的端口 proxy_pass http://localhost:后端实际运行端口; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } }

使用域名

要注意一点,使用了下面的配置,就不需要再更改宝塔前端项目的 Nginx 配置(不需要再添加任何配置,比如 proxy_pass 之类的),当然也不用单独再配置一个前端项目

类似这种

image.png
▼
nginx
复制代码
user root; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; access_log logs/access.log; sendfile on; keepalive_timeout 65; #gzip on; # 前端配置不是重点 server { listen 80; # 修改成前端的域名 server_name leikooo.cn; root /root/app/dist; # 访问默写前端页面 404 就是没加下面这行的原因 try_files $uri $uri/ /index.html; location / { index index.html index.htm; } } # 后端相关的反向代理+跨域 server { # 比如我们配置 backend.leikooo.cn 这个是后端域名 # http 默认 80 端口 https 默认 443 端口 listen 80; server_name 修改成自己的二级域名; # 我设置的一般就是 backend.leikooo.cn location / { # 禁止非 GET|POST|HEAD|OPTIONS|PUT|PATCH|DELET 的请求 if ( $request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|PATCH|DELETE)$ ) { return 444; } set $origin $http_origin; # 重点!比如: # $origin !~ '^http?://leikooo\.com$ # 下面配置前端域名,比如 leikooo、.cn if ($origin !~ '^http?://前端域名$') { set $origin '前端域名'; } if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' "$origin" always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Content-Type, Accept, Authorization' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header Access-Control-Max-Age 1728000; add_header Content-Type 'text/plain charset=UTF-8'; add_header Content-Length 0; return 204; } if ($request_method ~ '(GET|POST|PATCH|PUT|DELETE)') { add_header Access-Control-Allow-Origin "$origin" always; add_header Access-Control-Allow-Methods 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header Access-Control-Allow-Headers 'Content-Type, Accept, Authorization' always; add_header Access-Control-Allow-Credentials true always; } # 反向代理到后端具体运行的端口 # 如果后端开启了 https 不要忘记请求协议变成 https proxy_pass http://localhost:后端实际运行端口; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } }

注意:

1)前端请求 9090 而不是直接请求后端实际运行端口

2)服务器放行 9090 端口,这个要注意!!(具体看自己写的是哪个端口)

Nginx 解决跨域原理

  1. 跨域问题的原因 跨域问题是由浏览器的 同源策略(Same-Origin Policy) 引起的。同源策略要求: 协议、域名、端口都必须一致。 如果前端和后端运行在不同的域名、IP 或端口上,例如: 前端地址为 http://126.4.3.3 后端地址为 http://126.4.3.3:8080 浏览器会认为它们是不同源,因此会阻止请求,这是跨域问题的本质。
  2. 如何解决跨域问题的配置逻辑 Nginx 配置中,location /api 是关键:
▼
text
复制代码
location /api { proxy_pass http://127.0.0.1:后端端口; proxy_set_header Host $proxy_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; proxy_set_header Connection ""; }

这里通过 Nginx 的反向代理机制: 将前端通过 /api 路径发起的请求转发到后端(即 http://127.0.0.1:后端端口 )。 对于前端来说,请求的域名和端口仍然是 http://126.4.3.3 ,从浏览器的角度看,请求是同源的。

使用 HTTPS

实际测试使用域名 + HTTPS 也可以解决,解决无法设置 cookie 的问题

教程:https://www.codefather.cn/post/1831983737277050881

BUG

1、前端使用域名,但是前端后端使用 ip ,导致 session 设置不上

解决:前后端统一,要用域名都用域名、IP 都用 IP

2、还是不行?

1)检查端口是否放行!!! 2)前端请求的端口是否是 Nginx listen 的端口,不要直接请求实际端口 !!!

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
leikooo
作者分享
IP 数据库体积缩小 31%,关键竟然是合并重复子树
2
提到用 Rust 重写项目,很多人的第一反应往往是性能提升。以 Bun 为例,它最初主要采用 Zig 开发,后来在 2026 年发布的 v1.4 版本中将核心代码迁移到了 Rust。用 Rust 重写,性能、安全性往往是大家想要的,但是有没有想过用 Rust 重写还能发现 bug 呢? pgrust 这个项就是,他用 Rust 重新实现 PostgreSQL,保持通信协议和 SQL 方言兼容。为了确认新系统的行为和原版一致,开发者需要对着 PostgreSQL 的实际返回逐个比对。结果查着查着,有些异常不是新代码的锅,PostgreSQL 自己就是这样的。pgrust 开发者 Michael Malis 把其中几个报告给了 PostgreSQL 社区。 大概的问题: 1)SP-GiST 空间索引里有个函数判断点的方位,代码认为上、下、同一水平线三种情况已经穷尽了,末尾放了句「impossible case」兜底。结果在特定浮点边界附近,三种判断同时不成立,程序真走进了这个「不可能」的分支 2)日期格式缓存复用时,一个解析模式的标记没重置,导致同一条 JSONPath 查询在同一个会话里,换个执行顺序结果就不一样。 3)pgcrypto 的 crypt() 在某些输入下截断了多字节字符,返回的文本编码不合法。能存进去,但 pg_dump 导出时就炸了。 一个用 Rust 重写的新项目,测着测着,把运行了几十年的 PostgreSQL 里埋着的东西翻了出来。Rust 重写的价值,不一定只在跑得更快、更安全这一件事上。为了做到真正的兼容,开发者得把前人没怎么碰过的边界全走一遍,这个过程本身就在帮原版排雷。
4
ARTS 1025: 唯一字符与同构字符串、省词可能增加阅读负担与重新估算软件的实现成本
5
🎉 1024 程序员节快乐! 祝编程导航里的每一位程序员:offer 多多,薪资翻倍,面试顺利,早日拿下心仪 offer!💻
17
AST:从 Java 代码理解抽象语法树
5
下载 APP