跨域配置
背景介绍
链路传播协议允许在分布式系统中传递跟踪信息,通常通过在 HTTP 请求头中添加特殊的请求头来传递 traceId 和 spanId 等跟踪信息。
跨域资源共享 (CORS)
CORS(跨域资源共享)是一种机制,它使用附加的 HTTP 头来告诉浏览器让运行在一个来源(domain)的 Web 应用被准许访问另一个来源的资源。为了确保安全性,浏览器会对某些跨域请求(特别是带有自定义头的请求)执行预检请求(Preflight Request)。
预检请求(Preflight Request)
当使用 XMLHttpRequest 或 Fetch API 进行跨域请求时,如果请求使用了以下之一,则浏览器会先发送一个 OPTIONS 请求,称为预检请求,以检查服务器是否允许该实际请求:
- 使用了除 GET、POST 或 HEAD 之外的 HTTP 方法。
- 使用了自定义的请求头(即不是 CORS 规范中列出的标准头,如 Content-Type、Accept、Authorization 等)。
由于 OpenTelemetry 需要在请求头中添加自定义的跟踪信息(如 traceparent、tracestate),这会触发浏览器的预检请求。
示例
我们有一个听云跟踪头 X-Tingyun
,其格式如下:
X-Tingyun:c=B|b1t76Pq_FnI;x=4d42de860dc64124;u=base64#c3p3YW5zaGVuZw==
当浏览器发起包含此请求头的跨域请求时,会首先发送一个预检请求:
OPTIONS /api/resource HTTP/1.1
Host: api.example.com
Origin: http://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Tingyun
服务器需要响应允许该请求:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: X-Tingyun
如果服务器允许预检请求,浏览器会发送实际的请求:
POST /api/resource HTTP/1.1
Host: api.example.com
Origin: http://example.com
X-Tingyun:c=B|b1t76Pq_FnI;x=4d42de860dc64124;u=base64#c3p3YW5zaGVuZw==
配置说明
NGINX 配置示例
假设您的后端服务在 NGINX 之后运行。我们需要配置 NGINX 以处理预检请求并允许自定义请求头。
server {
listen 80;
server_name api.example.com;
# 定义一个位置块来处理预检请求
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'X-Tingyun';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
# 处理实际请求
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Expose-Headers' 'X-Tingyun-Data';
proxy_pass http://backend_service;
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;
}
}
配置说明
- 处理预检请求:在 location /api/ 块中,我们首先检查请求方法是否为 OPTIONS。如果是,则添加 CORS 相关的响应头,并返回 204 状态码。
- 添加响应头:对于所有请求(包括预检请求和实际请求),我们都添加了
Access-Control-Allow-Origin
、Access-Control-Allow-Methods
和Access-Control-Allow-Headers
头。这些头允许跨域请求并允许包含自定义头。 - Access-Control-Max-Age:用于配置预检请求的缓存时间,避免每次请求都发送预检请求。
- 'Access-Control-Expose-Headers' 'X-Tingyun-Data':该请求头用于生成WEB SDK可以获取返回性能数据,按固定格式配置即可。
Apache httpd 配置示例
开启mod_headers和mod_rewrite模块
在Apache中,CORS相关的头部设置需要依赖于mod_headers和mod_rewrite模块。首先,你需要确保该模块已经启用。在httpd.conf文件中找到以下行,并确保没有被注释掉。
LoadModule headers_module modules/mod_headers.so
LoadModule rewrite_module modules/mod_rewrite.so
配置跨域设置
<Location /api/>
# 处理OPTIONS预检请求
<IfModule mod_headers.c>
SetEnvIf Request_Method OPTIONS preflight
Header add Access-Control-Allow-Methods "GET, POST, OPTIONS" env=preflight
Header add Access-Control-Allow-Headers "X-Tingyun" env=preflight
Header add Access-Control-Max-Age 1728000 env=preflight
# 对OPTIONS请求返回204状态码
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^ - [R=204,L]
</IfModule>
# 对于其他请求,设置CORS头部
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Expose-Headers "X-Tingyun-Data"
</Location>
测试和验证
测试预检请求
使用工具(如 curl 或 Postman)发送一个带有自定义头的 OPTIONS 请求,确保服务器返回正确的 CORS 头。
curl -X OPTIONS -H "Origin: http://example.com" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: X-Tingyun" http://api.example.com/api/resource
实际请求
确认预检请求通过后,发送实际请求以验证所有头信息被正确处理。
curl -X POST -H "Origin: http://example.com" -H "c=B|b1t76Pq_FnI;x=4d42de860dc64124;u=base64#c3p3YW5zaGVuZw==" -H "Content-Type: application/json" -d '{"data": "example"}' http://api.example.com/api/resource
通过以上配置,NGINX 可以正确处理包含 听云请求头的跨域请求,同时符合 CORS 规范。
配置指南
Web SDK支持多个厂家和自定义链路传播请求头,所以需要按需配置