问题现象
我的博客底部嵌入了 Uptime Kuma 状态监控,某天突然发现显示变成了**"状态获取失败"**,而不是正常的"所有业务正常"。
打开浏览器开发者工具(F12),在控制台看到一条 CORS 报错,浏览器拦截了从博客到监控服务的跨域请求。
原因分析
这本质上是一个 CORS(跨域资源共享) 问题。
博客通过 JavaScript 的 fetch() 向 Uptime Kuma 请求状态数据。因为两个域名不同,浏览器要求目标服务器在响应头中明确允许跨域访问。
Uptime Kuma 本身运行正常,API 数据也在。但 Kuma 域名是通过 OpenResty(Nginx)反向代理到容器的,反向代理层没有返回 Access-Control-Allow-Origin 响应头,所以浏览器拦截了请求。
用 curl 直接验证可以看到确实缺少 CORS 头:
BASH
curl -sI -H "Origin: https://blog.example.com" \
"https://kuma.example.com/api/status-page/heartbeat/xxx"
尝试过的方案
方案一:1Panel 跨域访问 开关
1Panel 的网站设置中有一个"跨域访问"标签页,打开开关后会在 Nginx 的 server 块添加 add_header:
NGINX
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS,PUT,DELETE always;
但不生效。因为后端的 include 引入了反向代理的 location 块,Nginx 的 add_header 会被子级 location 块覆盖。
方案二:Uptime Kuma 容器环境变量
在 Docker 的 compose 里添加了 UPTIME_KUMA_CORS_ORIGIN 环境变量,但 Uptime Kuma v2.4.0 本身并不支持这个变量,容器重建后也没用。
最终方案:more_set_headers
OpenResty 自带 headers-more-nginx-module 模块,提供了 more_set_headers 指令。与 add_header 不同,它作用在输出过滤器层,不会被子级 location 块覆盖。
在 1Panel → 网站 → 对应域名 → 配置文件,将:
NGINX
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS,PUT,DELETE always;
替换为:
NGINX
more_set_headers "Access-Control-Allow-Origin: *";
more_set_headers "Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE";
完整配置文件参考:
NGINX
server {
listen 80;
listen 443 ssl;
server_name kuma.example.com;
index index.php index.html index.htm default.php default.htm default.html;
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md) {
return 404;
}
location ^~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
root /var/www/html;
http2 on;
if ($scheme = http) {
return 301 https://$host$request_uri;
}
ssl_certificate /path/to/ssl/fullchain.pem;
ssl_certificate_key /path/to/ssl/privkey.pem;
ssl_protocols TLSv1.3 TLSv1.2;
# ... ssl_ciphers 省略 ...
proxy_set_header X-Forwarded-Proto https;
add_header Strict-Transport-Security "max-age=31536000";
# CORS — 用 more_set_headers 避免被 proxy location 覆盖
more_set_headers "Access-Control-Allow-Origin: *";
more_set_headers "Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE";
if ($request_method = 'OPTIONS') {
return 204;
}
include /path/to/proxy/*.conf;
}
保存后重载(或重启)OpenResty,问题解决。
验证
再次用 curl 检查响应头:
BASH
curl -sI -H "Origin: https://blog.example.com" \
"https://kuma.example.com/api/status-page/heartbeat/xxx"
现在可以看到:
PLAINTEXT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE
回到博客页面,浏览器控制台不再有 CORS 报错,底部 Uptime Kuma 状态恢复显示"所有业务正常"。
总结
尝试 | 结果 | 原因 |
|---|---|---|
1Panel 跨域开关 | 不生效 | add_header 被 proxy location 覆盖 |
容器环境变量 | 不生效 | Uptime Kuma 不支持 |
more_set_headers | 生效 | 输出过滤器层,不被覆盖 |
核心教训:在 Nginx/OpenResty 配置中,如果你有 include 引入的 location 块(反向代理配置),不要在 server 级别用 add_header 设置跨域头——用 more_set_headers 代替。


