1
0
mirror of https://github.com/Mabbs/mabbs.github.io synced 2025-08-09 10:12:02 +00:00
blog/_posts/2025-08-01-sw-proxy.md
mayx 2c629c95b9 Update 2 files
- /assets/Mabbs.zip
- /_posts/2025-08-01-sw-proxy.md
2025-08-01 12:07:43 +00:00

110 lines
9.1 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
layout: post
title: 用Service Worker实现一个反向代理
tags: [浏览器, Service Worker, Worker, 反向代理]
---
现代浏览器真是强大,可以替代一些服务器的功能了!<!--more-->
# 起因
前段时间在和群友聊天的时候,提到了我博客的[分发方案](/2022/02/14/move.html),这么多年过去之后我已经在很多平台上[分发](/proxylist.html)了我的博客不过这只是多重冗余并不算去中心化虽然我也有向IPFS同步不过IPFS还得pin也不太可靠……所以这么看来我的博客似乎还不算极其可靠😂但其实不完全是这样。因为除了向不同平台的分发我的博客还有一个全文搜索的功能。更重要的是之前做[文章推荐功能](/2024/10/01/suggest.html)时会把整个博客所有文章的文字存到访客浏览器的localStorage中。这么说来只要有人访问了我博客的文章他们的浏览器中就会保存一份我博客文章的完整文本副本。从这个角度看可靠性应该算是相当高了吧
不过我之前的分发方案里还记录了一点在GitHub Pages以外的平台我还打包了一份全站生成后的代码之所以要全站打包也是希望我的博客能尽可能的分发考虑到几乎所有的Linux发行版一定有tar而不一定有zip所以我最终打包成了tgz格式。如果能让访客下载这个全站打包好的副本相比于浏览器里只存储了文章文字的全文数据这应该是一个更好的备份方式吧毕竟我的博客本身也是我的作品……所以这个压缩包到底有什么地方可以用到呢
这时候我想起来现代的浏览器功能已经非常强大了甚至在浏览器里直接运行一个Web服务器也完全没问题。如果能让访客在浏览器里下载那个压缩包并运行一个Web服务器那就相当于在他们本地设备上部署了一份我的博客副本。这样一来除了我自己搭建的网站之外这些访客的本地也运行着一个我的博客实例😆当然这份副本只有访客自己能看到
# 研究实现方案
想要在浏览器上运行Web服务器其实很简单那就是使用Service Worker它可以完全离线在浏览器上工作。格式的话和以前写过的Cloudflare Worker非常相似毕竟Cloudflare Worker就是模仿Service Worker的方式运行啊😂所以我要是想写Service Worker应该很简单。
有了执行的东西之后就是存储在Service Worker上存储可以用Cache Storage用它的话不仅可以保存文件的内容还可以保存响应头之类的东西用来和Service Worker配合使用非常的方便不过既然是Cache它的可靠性就不能保证了浏览器很可能在需要的时候清除缓存内容所以相比之下用IndexedDB应该会更可靠一些。
那么接下来就该处理我的tgz文件了tgz的本质是tar文件被gzip压缩之后的东西。浏览器解压gzip倒是简单可以用Compression Stream API但它也只能处理gzip了……对于tar的处理似乎就必须用第三方库。而tar的库在网上搜了搜似乎很少网上找了个[tarjs](https://github.com/gera2ld/tarjs)库文档写的也看不懂也很少看来是有这个需求的人很少啊而且还要用现代JS那种开发方式要用什么npm之类的。在[上一篇文章](/2025/07/24/screenshot.html)我就说过我不是专门写前端的对在自己电脑上安装Node.js之类的东西很反感。后来问AI也完全写不出能用的代码估计这个功能还是太小众了……另外又想到除了这个问题之外还要处理网站更新的时候该怎么通知Service Worker之类乱七八糟的事情……所以只好作罢😅。
# 使用Service Worker进行反向代理
这么看来离线运行我的博客似乎有点麻烦不过既然都研究了一下Service Worker不如想想其他能做的事情……比如当作反向代理虽然在浏览器上搞反向代理好像意义不是很大……但值得一试。我之前见过一个项目叫做[jsproxy](https://github.com/EtherDream/jsproxy)它是用Service Worker实现的正向代理这给了我一些启发。我在之前研究分发方案的时候发现了一些模仿GeoCities的复古静态网站托管平台比如[Neocities](https://neocities.org)和[Nekoweb](https://nekoweb.org)。它们需要通过网页或API才能上传网站不太方便使用CI/CD的方式部署。但是我又觉得它们的社区很有意思所以想用Service Worker的方式反代到我的网站显得我的网站是部署在它们上面一样。
这个做起来非常简单,其实就和我以前用[Cloudflare Worker搭建反代](/2021/03/02/workers.html#%E9%A6%96%E5%85%88%E7%BB%99%E8%87%AA%E5%B7%B1%E6%90%AD%E4%B8%AA%E5%8F%8D%E4%BB%A3)几乎完全一样遇到请求之后直接通过Fetch获取内容然后再返回就行唯一不同的就是浏览器存在跨域策略在跨域时只有对应网站存在合适的响应头才可以成功请求还好我用的Pages服务大多都允许跨域。但是在我实际测试的时候发现这个允许跨域的等级不太一样比如GitHub Pages的响应头里包含`Access-Control-Allow-Origin: *`但是不允许OPTIONS方式请求另外如果要修改请求头在响应头里还要一一允许相应的请求头才行……当然对于这种问题解决起来很简单就和我之前写的[订阅源预览](/2025/04/08/feed.html)一样,用[cloudflare-cors-anywhere](https://github.com/Zibri/cloudflare-cors-anywhere)搭建的CORS代理就可以有了这个就可以轻松使用Service Worker反代其他网站了。
当然对我来说其实有`Access-Control-Allow-Origin: *`就够了,我也不需要花里胡哨的请求方式,也不需要在请求头和请求体里加什么莫名其妙的东西,所以对我来说直接请求我的某一个镜像站就可以,于是代码如下:
**index.html**
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Mayx的博客</title>
</head>
<body>
<script>
// 注册 Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker 注册成功:', registration.scope);
// 刷新网页
location.reload();
})
.catch(error => {
console.error('Service Worker 注册失败:', error);
location="https://mabbs.github.io";
});
} else {
location="https://mabbs.github.io";
}
</script>
<h1>Redirecting&hellip;</h1>
<a href="https://mabbs.github.io">Click here if you are not redirected.</a>
</body>
</html>
```
**sw.js**
```javascript
const TARGET_SITE = '被反代的网站'; //也可以用CORS代理
self.addEventListener('install', event => {
// 强制立即激活新 Service Worker
event.waitUntil(self.skipWaiting());
});
self.addEventListener('activate', event => {
// 立即控制所有客户端
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', event => {
if (new URL(event.request.url).origin == self.location.origin) {
event.respondWith(handleProxyRequest(event.request));
}
});
async function handleProxyRequest(request) {
try {
// 构建目标 URL
const targetUrl = new URL(request.url);
const proxyUrl = TARGET_SITE + targetUrl.pathname + targetUrl.search;
// 创建新请求(复制原请求属性)
const proxyRequest = new Request(proxyUrl, {
method: request.method,
// headers: request.headers,
// body: request.body
});
// 发送代理请求
const response = await fetch(proxyRequest);
// 返回修改后的响应
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
} catch (error) {
console.error('Proxy error:', error);
return new Response('Proxy failed', { status: 500 });
}
}
```
最终的实际效果: <https://mayx.nekoweb.org>
# 感想
虽然折腾了半天没能增强我博客的可靠性……但是体会到了现代浏览器的强大之处难怪前几年会提出ChromeOS和PWA之类的东西原来浏览器功能还是相当强大的用了Service Worker以后即使是纯前端也可以有和使用服务器一样的体验在过去的浏览器中要是想实现这样的功能……好像也不是不可能😂用AJAX加服务器使用伪静态策略其实是可以做到的……其实Service Worker的功能更多还是在离线时使用的我这个例子好像没体现它的优势😆。
但总的来说相比以前想要实现这种反代的功能代码还是更清晰也更简单了也许以后如果有机会我又有心思让博客在访客浏览器上离线运行那就可以体现Service Worker真正的优势了🤣。