Nginx反向代理解决跨域问题详解

Nginx反向代理解决跨域问题详解

核心原理

Nginx反向代理解决跨域的核心思路是让客户端请求同域名下的接口,由Nginx将请求转发到目标服务器,从而规避浏览器的同源策略限制。

复制代码
客户端(同源:www.domain.com)
        ↓
    Nginx(同源:www.domain.com)
        ↓
目标服务器(跨域:api.external.com)

完整配置与代码示例

基本反向代理配置

nginx 复制代码
# /etc/nginx/conf.d/default.conf

server {
    listen 80;
    server_name mydomain.com;  # 前端域名
    
    # 前端静态资源
    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
    
    # 接口代理配置
    location /api {
        # 后端实际地址(跨域的目标服务器)
        proxy_pass http://api.external.com;
        
        # 设置必要的请求头
        proxy_set_header Host $proxy_host;  # 保留原始Host
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # CORS支持(可选)
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        
        # 处理OPTIONS预检请求
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        }
    }
}

关键配置解析

  1. proxy_pass

    nginx 复制代码
    location /api {
        proxy_pass http://api.external.com;
    }
    • 所有以/api开头的请求都会被转发到http://api.external.com
  2. 请求头保留

    nginx 复制代码
    proxy_set_header Host $proxy_host;      # 保留原始主机头
    proxy_set_header X-Real-IP $remote_addr; # 保留客户端真实IP
  3. CORS支持

    nginx 复制代码
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    • 直接从Nginx层支持CORS,后端服务不需要额外处理
  4. OPTIONS请求处理

    nginx 复制代码
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 1728000;
        # ...
        return 204;
    }
    • 直接响应预检请求,减轻后端服务器压力

常见场景定制配置

场景1:URL重写(删除API前缀)
nginx 复制代码
location /api {
    # 重写路径:移除/api前缀
    rewrite ^/api/(.*)$ /$1 break;
    proxy_pass http://api.external.com;
}
场景2:添加API前缀
nginx 复制代码
location /user-service {
    # 添加/api前缀
    rewrite ^/user-service/(.*)$ /api/$1 break;
    proxy_pass http://api.external.com;
}
场景3:负载均衡
nginx 复制代码
upstream backend {
    server backend1.example.com:8080 weight=3;  # 权重
    server backend2.example.com:8081;
    server backup.example.com:8082 backup;       # 备用服务器
}

location /api {
    proxy_pass http://backend;  # 使用负载均衡器
    proxy_set_header Host $host;
}
场景4:WebSocket支持
nginx 复制代码
location /socket {
    proxy_pass http://ws-server.com;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

前端代码示例

javascript 复制代码
// 请求代理路径(不再需要直接访问跨域地址)
const API_BASE = '/api'; // 与Nginx配置中的location匹配

// GET请求示例
async function getData() {
  try {
    const response = await fetch(`${API_BASE}/data`, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json'
      }
    });
    return await response.json();
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}

// POST请求示例
async function postData(data) {
  try {
    const response = await fetch(`${API_BASE}/save`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify(data)
    });
    return await response.json();
  } catch (error) {
    console.error('Error posting data:', error);
  }
}

完整操作流程

  1. 安装Nginx

    bash 复制代码
    # Ubuntu/Debian
    sudo apt update
    sudo apt install nginx
    
    # CentOS/RHEL
    sudo yum install epel-release
    sudo yum install nginx
  2. 编辑配置文件

    bash 复制代码
    sudo nano /etc/nginx/conf.d/default.conf

    添加上面的代理配置

  3. 测试配置

    bash 复制代码
    sudo nginx -t

    输出 syntax is oktest is successful 表示配置正确

  4. 重启Nginx

    bash 复制代码
    sudo systemctl restart nginx
    # 或
    sudo service nginx restart
  5. 部署前端项目

    bash 复制代码
    # 将前端构建文件放入指定目录
    sudo cp -R /path/to/dist /usr/share/nginx/html
  6. 验证访问

    访问 http://your-domain.com 应该加载前端页面,所有API请求自动代理到目标服务

优势对比

方案 是否需要修改代码 安全性 性能影响 多服务支持
Nginx反向代理 ⭐⭐⭐⭐⭐ 几乎没有 ⭐⭐⭐⭐⭐
CORS ⭐⭐⭐⭐ 中等 ⭐⭐
JSONP
Webpack代理 ✘ (仅开发) ⭐⭐ 低(开发) ⭐⭐

注意事项

  1. 路径匹配

    • 确保location指令的路径匹配模式与前端请求一致
    • 使用正则表达式处理复杂的URL模式
  2. 日志调试

    nginx 复制代码
    location /api {
        proxy_pass http://api.external.com;
        access_log /var/log/nginx/api-access.log;
        error_log /var/log/nginx/api-error.log;
    }
  3. 超时设置

    nginx 复制代码
    proxy_connect_timeout 60s;   # 连接超时
    proxy_send_timeout 60s;       # 发送超时
    proxy_read_timeout 180s;      # 读取超时
  4. Cookie传递

    nginx 复制代码
    proxy_cookie_domain api.external.com mydomain.com;
    proxy_cookie_path / /api/;
  5. 安全限制

    • 避免完全开放的跨域(Access-Control-Allow-Origin: *)
    • 建议使用具体的域名白名单

Nginx反向代理是当前解决跨域问题最成熟、稳定、高性能的解决方案,特别适合生产环境使用,既能解决跨域问题,又能实现负载均衡、安全防护等额外好处。

相关推荐
乘云数字DATABUFF3 天前
5分钟部署开源APM Databuff:OpenTelemetry全链路追踪入门实战
运维·后端
荣--5 天前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森5 天前
动手实战学 Docker — 从零到集群编排完全指南
运维
Avan_菜菜6 天前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
SelectDB7 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
XIAOHEZIcode8 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220709 天前
如何搭建本地yum源(上)
运维
ping某10 天前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
大树8812 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠12 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql