跨域请求是指在浏览器中,使用 JavaScript 向不同域名的服务器发送 HTTP 请求。这在现代 Web 开发中非常常见,但由于安全原因,浏览器默认限制跨域请求。为了实现跨域访问,有几种常见的解决方案:

一、CORS(Cross-Origin Resource Sharing)

CORS 是一种现代的跨域解决方案,通过 HTTP 头来允许服务器声明哪些来源可以访问资源。浏览器会根据服务器返回的 CORS 头信息决定是否允许跨域请求。

优点

  • 支持多种 HTTP 方法(GET、POST、PUT、DELETE 等)。
  • 安全性较高,能细粒度控制哪些来源可以访问资源。

缺点

需要服务器端配置。

CORS 的工作流程如下:

  1. 浏览器向服务器发送 AJAX 请求,并在请求头中添加一个 Origin 字段,表示请求的来源。
  2. 服务器接收到请求后,检查 Origin 字段,判断请求是否来自允许的域名。如果请求来自允许的域名,则在响应头中添加一个 Access-Control-Allow-Origin 字段,表示允许该域名访问资源。
  3. 浏览器接收到响应后,检查 Access-Control-Allow-Origin 字段,判断响应是否允许当前域名访问。如果允许访问,则浏览器将响应传递给 JavaScript 代码,否则抛出一个错误。

CORS 的优点是安全性高,可以避免跨站点脚本攻击(XSS)和跨站点请求伪造(CSRF)等安全问题。缺点是需要服务器支持,而且需要在响应头中添加一些额外的字段,增加了服务器的负担。

二、JSONP(JSON with Padding)

JSONP 是一种早期的跨域解决方案,主要用于 GET 请求。它利用 <script> 标签可以跨域加载资源的特性,通过动态创建 <script> 标签来实现跨域请求。

优点

  • 简单易用,不需要服务器端配置。

缺点

  • 只支持 GET 请求,无法使用 POST、PUT、DELETE 等其他 HTTP 方法。
  • 容易遭受 XSS 攻击。

示例

客户端:

<script>
function handleResponse(data) {
    console.log(data);
}

var script = document.createElement('script');
script.src = 'https://example.com/api?callback=handleResponse';
document.body.appendChild(script);
</script>

服务器端:

<?php
$callback = $_GET['callback'];
$data = array('message' => 'Hello, World!');
echo $callback . '(' . json_encode($data) . ')';
?>

三、服务器端代理

服务器端代理是一种通过在同域名的服务器上设置一个代理,将跨域请求转发到目标服务器的解决方案。这样客户端只需要与同域名的服务器通信,避免了跨域问题。

优点

  • 可以完全避免跨域问题。
  • 支持所有 HTTP 方法。

缺点

  • 需要额外的服务器配置和资源。

四、Nginx 反向代理

Nginx 反向代理是一种通过在 Nginx 服务器上配置反向代理,将跨域请求转发到目标服务器的解决方案。这样客户端只需要与同域名的 Nginx 服务器通信,避免了跨域问题。

优点

  • 可以完全避免跨域问题。
  • 支持所有 HTTP 方法。
  • Nginx 配置灵活,性能高。

缺点

  • 需要额外的服务器配置和资源。
server {
    listen 80;
    server_name yourdomain.com;

    location /api/ {
        proxy_pass https://example.com/api/;
        proxy_set_header 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;
    }
}

常见跨域报错

如果设置多个Access-Control-Allow-Origin也会报错

Access to XMLHttpRequest at ‘https://upload.tapd.cn/index.php?action=upload’ from origin ‘https://www.tapd.cn’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: The ‘Access-Control-Allow-Origin’ header contains multiple values ‘*, https://www.tapd.cn’, but only one is allowed.