浏览器的同源限制解决办法:跨域和nginx代理

跨域访问示例与解决方案

示例场景

  • 前端服务器域名:https://front.example.com
  • 后端服务器域名:https://api.backend.com
  • 浏览器访问前端页面时,前端代码通过AJAX请求后端接口,触发跨域问题(因域名不同)。

通俗来讲,同源就是指浏览器只支持前端页面的源请求目标的源(后端源)一致(协议、域名、端口都得相同)的访问,如果浏览器通过前端页面去访问和前端不在一个域名的其他服务器,会被认为是恶意行为被阻止。


方法1:同源化,前端服务器设置nginx反向代理

如果前端文件里的fetch都没用后端的绝对地址fetch(https://api.backend.com:443/api1),而是用比如fetch(/api1),那么应该在前端服务器上设置的nginx配置里不仅写上前端静态文件的位置,还要写上/api1的反向代理位置https://api.backend.com,由nginx去完成代理。这时对浏览器来说前端文件和api1请求都是对nginx代理留出的接口https://front.example.com:443,不存在跨域问题。这是标准的解决办法。

注意:这时用户浏览器去请求后端/api1,实际上是用户浏览器先请求前端的https://front.example.com:443/api1,然后前端的nginx代理再去请求后端https://api.backend.com:443/api1并转回给用户浏览器。这要求前端服务器必须能连接上后端服务器。这种方案不是用户浏览器直接请求后端服务器。

方法2:CORS(后端配置允许跨域)

如果前端文件的fetch写死了后端地址fetch(https://api.backend.com:443/api1),那么用户浏览器是直接请求后端服务器,没有经过前端服务器代理。

举个例子,比如前端服务器只是提供一个静态网站,告诉你后端服务器的位置,要用户浏览器自己直接去下载后端服务器上的内容(比如github的raw文件就是这样的),此时前端服务器不参与后端的代理,全凭用户浏览器自己直接去下载后端内容。

但由于前端页面的静态文件是在前端服务器上,用户浏览器向后端服务器发送请求时带上了Origin: https://front.example.com:443的前端地址的header。此时浏览器发现请求地址是后端服务器https://api.backend.com:443,但是前端页面的源是https://front.example.com:443,为了安全浏览器会阻止这个跨域请求(只有浏览器有这个限制,自己手动curl是不会的)。

为了解决这个问题,后端服务器需在响应头中明确允许前端域名访问

Access-Control-Allow-Origin: https://front.example.com  
Access-Control-Allow-Methods: GET, POST, OPTIONS  
Access-Control-Allow-Headers: Content-Type  

如果后端服务器是用了nginx暴露域名端口,那上面这个CORS配置加在nginx的配置里;如果后端是fastapi server直接用uvicorn直接暴露0.0.0.0(不建议,而且没http),则是在fastapi server的main.py里面加api.add_middleware(CORSMiddleware,allow_origins=xxxx)

关键点

  • 浏览器会自动在跨域请求的Origin头中携带前端域名(如Origin: https://front.example.com)。
  • 后端需动态或静态配置允许的域名,匹配后才返回正确响应。

缺点:

  • 后端服务器需要明确知道前端服务器的域名,否则只能设置为所有域名都allow(也就是设置为*,比如github的raw文件服务器就是这样的,他不可能预先知道所有要下载他文件的用户浏览器的IP和端口,所以只能设置为*),但这样直接暴露到公网是非常危险的,设计cookie、token等的不要用这种方法,只用来托管一些静态公共文件的后端服务器的api。
  • 后端仍然需要其他方法实现https(一般后端是写http的,再加nginx或者caddy代理实现https,不过不是本文讨论的范畴)

前后端同一服务器的情况

将前端和后端部署在同一服务器,配置nginx有两个作用:

  1. 托管前端网页静态文件,访问nginx暴露的端口地址会默认跳转访问前端页面资源位置。
  2. 后端api代理,将前端里对/api的请求转发到实际的后端地址。此时由于后端也在这台机器上,可以用127.0.0.1:8000(后端位置)。或者后端如果在另一台机器的话可以填那台机器上后端服务器的地址,但是必须得连的上。由于浏览器访问的前端页面的源和请求目标都给到了nginx的地址,所以浏览器看来是同源的,不存在跨域问题。

通过Nginx统一暴露端口(如443),并代理转发请求:

server {
    listen 443 ssl;
    server_name proxy.example.com;

    # 前端静态资源
    location / {
        root /var/www/frontend;
        index index.html;
    }

    # 后端API代理(后端只用在本机http://127.0.0.1:8000开放服务
    location /api/ {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
    }
}

效果

  • 浏览器访问https://proxy.example.com(前端)和https://proxy.example.com/api/xxx(后端)为同源。
  • Nginx隐藏后端真实地址,实际转发请求到http://127.0.0.1:8000
  • 后端只是http,但由于nginx暴露的域名是https的,所以外界仍然是https访问,不需要对后端做https处理。

对比

  • CORS:需后端配合,适合前后端独立维护的场景。
  • Nginx代理:无跨域问题,适合全栈可控的部署架构。(除了nginx,caddy也行,而且配置https证书更简单)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值