Skip to content

跨域配置

跨域资源共享(CORS)允许浏览器向跨源服务器发出XMLHttpRequest请求。

基本配置

简单配置

nginx
add_header 'Access-Control-Allow-Origin' '*';

指定域名

nginx
add_header 'Access-Control-Allow-Origin' 'https://www.example.com';

完整CORS配置

标准配置

nginx
server {
    listen 80;
    server_name api.example.com;

    # CORS头
    add_header 'Access-Control-Allow-Origin' 'https://www.example.com' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    add_header 'Access-Control-Max-Age' 1728000 always;

    # OPTIONS请求
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://www.example.com';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }

    location / {
        proxy_pass http://backend;
    }
}

多域名配置

允许多个域名

nginx
map $http_origin $cors_origin {
    default "";
    "https://www.example.com" $http_origin;
    "https://api.example.com" $http_origin;
}

server {
    listen 80;
    server_name api.example.com;

    add_header 'Access-Control-Allow-Origin' $cors_origin always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;

    location / {
        proxy_pass http://backend;
    }
}

静态资源跨域

字体文件

nginx
location ~* \.(woff|woff2|ttf|otf|eot)$ {
    add_header Access-Control-Allow-Origin "*";
    add_header Access-Control-Allow-Methods "GET, OPTIONS";
    add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";

    expires 1y;
    add_header Cache-Control "public, immutable";
}

图片文件

nginx
location ~* \.(jpg|jpeg|png|gif|webp|svg)$ {
    add_header Access-Control-Allow-Origin "*";

    expires 30d;
    add_header Cache-Control "public";
}

API跨域

RESTful API

nginx
server {
    listen 80;
    server_name api.example.com;

    # CORS配置
    add_header 'Access-Control-Allow-Origin' 'https://www.example.com' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;

    # OPTIONS请求
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://www.example.com';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }

    location /api/ {
        proxy_pass http://backend;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

预检请求

处理OPTIONS请求

nginx
if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' 'https://www.example.com';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
    add_header 'Access-Control-Max-Age' 1728000;
    add_header 'Content-Type' 'text/plain; charset=utf-8';
    add_header 'Content-Length' 0;
    return 204;
}

凭证请求

允许携带凭证

nginx
add_header 'Access-Control-Allow-Origin' 'https://www.example.com' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;

注意: 允许凭证时,Access-Control-Allow-Origin不能设置为"*"

完整示例

生产环境配置

nginx
map $http_origin $cors_origin {
    default "";
    "https://www.example.com" $http_origin;
    "https://admin.example.com" $http_origin;
}

server {
    listen 80;
    server_name api.example.com;

    # CORS配置
    add_header 'Access-Control-Allow-Origin' $cors_origin always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,X-Custom-Header' always;
    add_header 'Access-Control-Max-Age' 1728000 always;

    # OPTIONS请求
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' $cors_origin;
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }

    location /api/ {
        proxy_pass http://backend;

        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设置错误

解决: 检查Origin头

nginx
add_header 'Access-Control-Allow-Origin' 'https://www.example.com';

预检请求失败

原因: 未正确处理OPTIONS请求

解决: 添加OPTIONS请求处理

nginx
if ($request_method = 'OPTIONS') {
    return 204;
}

凭证请求失败

原因: Access-Control-Allow-Origin设置为"*"

解决: 设置具体域名

nginx
add_header 'Access-Control-Allow-Origin' 'https://www.example.com';
add_header 'Access-Control-Allow-Credentials' 'true';

总结

跨域配置的关键点:

  • Access-Control-Allow-Origin:设置允许的源
  • Access-Control-Allow-Methods:设置允许的方法
  • Access-Control-Allow-Headers:设置允许的请求头
  • OPTIONS请求:正确处理预检请求
  • 凭证请求:允许携带凭证

合理配置CORS,解决跨域访问问题。