PWA中用Fetch拦截跨域OPTIONS预检失败怎么办?

A. 窅恒 阅读 66

我在开发PWA时用service worker拦截fetch请求,发现跨域请求的OPTIONS预检总是返回504。尝试用event.respondWith(new Response())模拟响应后,控制台报“Response not allowed by CORS policy”。

代码是这样写的:


self.addEventListener('fetch', (event) => {
  if (event.request.method === 'OPTIONS') {
    event.respondWith(new Response(null, {
      headers: { 'Access-Control-Allow-Origin': '*' }
    }))
  }
});

但后端明明返回了正确的CORS头,为什么预检拦截后主请求还是失败?是不是拦截OPTIONS请求的方式有问题?

我来解答 赞 9 收藏
二维码
手机扫码查看
2 条解答
宇文子涵
你这个问题出在拦截OPTIONS预检时只加了 Access-Control-Allow-Origin,但CORS预检要求的头不止这一个。浏览器看到响应不完整,就会报“Response not allowed by CORS policy”。

正确做法是:如果你要在Service Worker里处理预检请求,必须返回完整的CORS响应头,而且状态码得是200或204。

改成这样:

self.addEventListener('fetch', (event) => {
if (event.request.method === 'OPTIONS') {
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers': '*',
'Access-Control-Max-Age': '86400' // 缓存一天
};
event.respondWith(new Response(null, {
status: 204,
headers
}));
}
});


不过更推荐的做法是:不要在Service Worker里自己处理OPTIONS,让后端直接返回正确的CORS头,你只拦截非预检请求。因为预检是浏览器自动发的,你强行拦截反而容易出问题。

如果确实要拦截,至少要确保你模拟的响应包含全部CORS相关头部,并且用204状态码——这是后端处理这类请求的常规方式。你现在只回了一个Origin,浏览器当然不认。
点赞 6
2026-02-09 14:05
书生シ桂香
问题应该出在你手动构造的OPTIONS响应上。浏览器的CORS预检机制对OPTIONS响应的要求非常严格,不是简单加个 Access-Control-Allow-Origin 就能搞定的。

浏览器需要完整的OPTIONS响应,包括状态码、必要的CORS头和方法列表。你现在的写法少了关键的状态码和必须的头信息,导致主请求时浏览器判断不符合CORS策略。

正确的做法是不要在Service Worker里拦截OPTIONS请求,让真正的跨域预检请求直接到达后端服务器。如果一定要拦截,得完全模拟一个合法的OPTIONS响应,这很麻烦也很容易出错。

所以最简单的解决办法就是:移除对OPTIONS请求的拦截逻辑,改成这样:

self.addEventListener('fetch', (event) => {
if (event.request.method !== 'OPTIONS') {
// 只拦截非OPTIONS的请求
event.respondWith(
fetch(event.request).then(response => {
return new Response(response.body, {
headers: {
...response.headers,
'Access-Control-Allow-Origin': '*'
}
});
})
);
}
});


这样就能保证OPTIONS预检请求正常通过,主请求也能正确处理CORS了。
点赞 8
2026-02-01 08:19