Files
pages/2025/08/01/sw-proxy.html
2025-12-31 16:00:29 +00:00

402 lines
32 KiB
HTML
Raw Permalink 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.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Begin Jekyll SEO tag v2.8.0 -->
<title>用Service Worker实现一个反向代理 | Mayx的博客</title>
<meta name="generator" content="Jekyll v3.9.5" />
<meta property="og:title" content="用Service Worker实现一个反向代理" />
<meta name="author" content="mayx" />
<meta property="og:locale" content="zh_CN" />
<meta name="description" content="现代浏览器真是强大,可以替代一些服务器的功能了!" />
<meta property="og:description" content="现代浏览器真是强大,可以替代一些服务器的功能了!" />
<meta property="og:site_name" content="Mayx的博客" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2025-08-01T00:00:00+08:00" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="用Service Worker实现一个反向代理" />
<meta name="google-site-verification" content="huTYdEesm8NaFymixMNqflyCp6Jfvd615j5Wq1i2PHc" />
<meta name="msvalidate.01" content="0ADFCE64B3557DC4DC5F2DC224C5FDDD" />
<meta name="yandex-verification" content="fc0e535abed800be" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BlogPosting","author":{"@type":"Person","name":"mayx"},"dateModified":"2025-08-01T00:00:00+08:00","datePublished":"2025-08-01T00:00:00+08:00","description":"现代浏览器真是强大,可以替代一些服务器的功能了!","headline":"用Service Worker实现一个反向代理","mainEntityOfPage":{"@type":"WebPage","@id":"/2025/08/01/sw-proxy.html"},"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://avatars0.githubusercontent.com/u/17966333"},"name":"mayx"},"url":"/2025/08/01/sw-proxy.html"}</script>
<!-- End Jekyll SEO tag -->
<link rel="canonical" href="https://mabbs.github.io/2025/08/01/sw-proxy.html" />
<link type="application/atom+xml" rel="alternate" href="/atom.xml" title="Mayx的博客" />
<link rel="alternate" type="application/rss+xml" title="Mayx的博客(RSS)" href="/rss.xml" />
<link rel="alternate" type="application/json" title="Mayx的博客(JSON Feed)" href="/feed.json" />
<link rel="stylesheet" href="/assets/css/style.css?v=1767196818" />
<!--[if !IE]> -->
<link rel="stylesheet" href="/Live2dHistoire/live2d/css/live2d.css" />
<!-- <![endif]-->
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Mayx的博客" />
<link rel="webmention" href="https://webmention.io/mabbs.github.io/webmention" />
<link rel="pingback" href="https://webmention.io/mabbs.github.io/xmlrpc" />
<link rel="preconnect" href="https://summary.mayx.eu.org" crossorigin="anonymous" />
<link rel="prefetch" href="https://www.blogsclub.org/badge/mabbs.github.io" as="image" />
<link rel="blogroll" type="text/xml" href="/blogroll.opml" />
<link rel="me" href="https://github.com/Mabbs" />
<script src="/assets/js/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-ajaxtransport-xdomainrequest/1.0.3/jquery.xdomainrequest.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<script>
var lastUpdated = new Date("Thu, 01 Jan 2026 00:00:18 +0800");
var BlogAPI = "https://summary.mayx.eu.org";
</script>
<script src="/assets/js/main.js"></script>
<!--[if !IE]> -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-137710294-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-137710294-1');
</script>
<script src="/assets/js/instant.page.js" type="module"></script>
<!-- <![endif]-->
</head>
<body>
<!--[if !IE]> --><noscript><marquee style="top: -15px; position: relative;"><small>发现当前浏览器没有启用JavaScript这不影响你的浏览但可能会有一些功能无法使用……</small></marquee></noscript><!-- <![endif]-->
<!--[if IE]><marquee style="top: -15px; position: relative;"><small>发现当前浏览器为Internet Explorer这不影响你的浏览但可能会有一些功能无法使用……</small></marquee><![endif]-->
<div class="wrapper">
<header class="h-card">
<h1><a class="u-url u-uid p-name" rel="me" href="/">Mayx的博客</a></h1>
<img src="https://avatars0.githubusercontent.com/u/17966333" fetchpriority="high" class="u-photo" alt="Logo" style="width: 90%; max-width: 300px; max-height: 300px;" />
<p class="p-note">Mayx's Home Page</p>
<form action="/search.html">
<input type="text" name="keyword" id="search-input-all" placeholder="Search blog posts.." />&#160;<input type="submit" value="搜索" />
</form>
<br />
<p class="view"><a class="u-url" href="/Mabbs/">About me</a></p>
<ul class="downloads">
<li style="width: 270px; border-right: none;"><a href="/MayxBlog.tgz">Download <strong>TGZ File</strong></a></li>
</ul>
</header>
<section class="h-entry">
<small><time class="date dt-published" datetime="2025-08-01T00:00:00+08:00">1 August 2025</time> - 字数统计4085 - 阅读大约需要14分钟 - Hits: <span id="/2025/08/01/sw-proxy.html" class="visitors">Loading...</span></small>
<h1 class="p-name">用Service Worker实现一个反向代理</h1>
<p class="view">by <a class="p-author h-card" href="//github.com/Mabbs">mayx</a></p>
<div id="outdate" style="display:none;">
<hr /><p>
这是一篇创建于 <span id="outime"></span> 天前的文章,其中的信息可能已经有所发展或是发生改变。
</p>
</div>
<script>
daysold = Math.floor((new Date().getTime() - new Date("Fri, 01 Aug 2025 00:00:00 +0800").getTime()) / (24 * 60 * 60 * 1000));
if (daysold > 90) {
document.getElementById("outdate").style.display = "block";
document.getElementById("outime").innerHTML = daysold;
}
</script>
<hr />
<b>AI摘要</b>
<p id="ai-output">这篇文章介绍了作者如何利用Service Worker在现代浏览器中实现一个简单的反向代理功能以提供博客的备份和离线访问。作者原本希望通过Service Worker在用户浏览器中运行一个Web服务器来存储博客副本但发现 tar.gz 文件的处理需要第三方库且难以找到适用的解决方案尤其是对于tar文件的处理。作者最终选择使用Service Worker作为反向代理将请求转发到其他网站如GeoCities风格的静态网站托管平台实现了类似的效果。这个过程中作者体验到了浏览器功能的强大并认为Service Worker在离线场景中有更大的潜力尽管在他的例子中并没有充分展示这种优势。</p>
<hr />
<ul><li><a href="#起因">起因</a></li><li><a href="#研究实现方案">研究实现方案</a></li><li><a href="#使用service-worker进行反向代理">使用Service Worker进行反向代理</a></li><li><a href="#感想">感想</a></li></ul>
<hr />
<main class="post-content e-content" role="main"><p>现代浏览器真是强大,可以替代一些服务器的功能了!<!--more--></p>
<h1 id="起因">
<a href="#起因"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 起因
</h1>
<p>前段时间在和群友聊天的时候,提到了我博客的<a href="/2022/02/14/move.html">分发方案</a>,这么多年过去之后我已经在很多平台上<a href="/proxylist.html">分发</a>了我的博客不过这只是多重冗余并不算去中心化虽然我也有向IPFS同步不过IPFS还得pin也不太可靠……所以这么看来我的博客似乎还不算极其可靠😂但其实不完全是这样。因为除了向不同平台的分发我的博客还有一个全文搜索的功能。更重要的是之前做<a href="/2024/10/01/suggest.html">文章推荐功能</a>会把整个博客所有文章的文字存到访客浏览器的localStorage中。这么说来只要有人访问了我博客的文章他们的浏览器中就会保存一份我博客文章的完整文本副本。从这个角度看可靠性应该算是相当高了吧 </p><p>
不过我之前的分发方案里还记录了一点在GitHub Pages以外的平台我还打包了一份全站生成后的代码之所以要全站打包也是希望我的博客能尽可能的分发考虑到几乎所有的Linux发行版一定有tar而不一定有zip所以我最终打包成了tgz格式。如果能让访客下载这个全站打包好的副本相比于浏览器里只存储了文章文字的全文数据这应该是一个更好的备份方式吧毕竟我的博客本身也是我的作品……所以这个压缩包到底有什么地方可以用到呢 </p><p>
这时候我想起来现代的浏览器功能已经非常强大了甚至在浏览器里直接运行一个Web服务器也完全没问题。如果能让访客在浏览器里下载那个压缩包并运行一个Web服务器那就相当于在他们本地设备上部署了一份我的博客副本。这样一来除了我自己搭建的网站之外这些访客的本地也运行着一个我的博客实例😆当然这份副本只有访客自己能看到</p>
<h1 id="研究实现方案">
<a href="#研究实现方案"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 研究实现方案
</h1>
<p>想要在浏览器上运行Web服务器其实很简单那就是使用Service Worker它可以完全离线在浏览器上工作。格式的话和以前写过的Cloudflare Worker非常相似毕竟Cloudflare Worker就是模仿Service Worker的方式运行啊😂所以我要是想写Service Worker应该很简单。 </p><p>
有了执行的东西之后就是存储在Service Worker上存储可以用Cache Storage用它的话不仅可以保存文件的内容还可以保存响应头之类的东西用来和Service Worker配合使用非常的方便不过既然是Cache它的可靠性就不能保证了浏览器很可能在需要的时候清除缓存内容所以相比之下用IndexedDB应该会更可靠一些。 </p><p>
那么接下来就该处理我的tgz文件了tgz的本质是tar文件被gzip压缩之后的东西。浏览器解压gzip倒是简单可以用Compression Stream API但它也只能处理gzip了……对于tar的处理似乎就必须用第三方库。而tar的库在网上搜了搜似乎很少网上找了个<a href="https://github.com/gera2ld/tarjs">tarjs</a>文档写的也看不懂也很少看来是有这个需求的人很少啊而且还要用现代JS那种开发方式要用什么npm之类的。在<a href="/2025/07/24/screenshot.html">上一篇文章</a>我就说过我不是专门写前端的对在自己电脑上安装Node.js之类的东西很反感。后来问AI也完全写不出能用的代码估计这个功能还是太小众了……另外又想到除了这个问题之外还要处理网站更新的时候该怎么通知Service Worker之类乱七八糟的事情……所以只好作罢😅。</p>
<h1 id="使用service-worker进行反向代理">
<a href="#使用service-worker进行反向代理"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 使用Service Worker进行反向代理
</h1>
<p>这么看来离线运行我的博客似乎有点麻烦不过既然都研究了一下Service Worker不如想想其他能做的事情……比如当作反向代理虽然在浏览器上搞反向代理好像意义不是很大……但值得一试。我之前见过一个项目叫做<a href="https://github.com/EtherDream/jsproxy">jsproxy</a>它是用Service Worker实现的正向代理这给了我一些启发。我在之前研究分发方案的时候发现了一些模仿GeoCities的复古静态网站托管平台比如<a href="https://neocities.org">Neocities</a><a href="https://nekoweb.org">Nekoweb</a>。它们需要通过网页或API才能上传网站不太方便使用CI/CD的方式部署。但是我又觉得它们的社区很有意思所以想用Service Worker的方式反代到我的网站显得我的网站是部署在它们上面一样。 </p><p>
这个做起来非常简单,其实就和我以前用<a href="/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">Cloudflare Worker搭建反代</a>几乎完全一样遇到请求之后直接通过Fetch获取内容然后再返回就行唯一不同的就是浏览器存在跨域策略在跨域时只有对应网站存在合适的响应头才可以成功请求还好我用的Pages服务大多都允许跨域。但是在我实际测试的时候发现这个允许跨域的等级不太一样比如GitHub Pages的响应头里包含<code class="language-plaintext highlighter-rouge">Access-Control-Allow-Origin: *</code>但是不允许OPTIONS方式请求另外如果要修改请求头在响应头里还要一一允许相应的请求头才行……当然对于这种问题解决起来很简单就和我之前写的<a href="/2025/04/08/feed.html">订阅源预览</a>一样,用<a href="https://github.com/Zibri/cloudflare-cors-anywhere">cloudflare-cors-anywhere</a>搭建的CORS代理就可以有了这个就可以轻松使用Service Worker反代其他网站了。 </p><p>
当然对我来说其实有<code class="language-plaintext highlighter-rouge">Access-Control-Allow-Origin: *</code>就够了,我也不需要花里胡哨的请求方式,也不需要在请求头和请求体里加什么莫名其妙的东西,所以对我来说直接请求我的某一个镜像站就可以,于是代码如下: </p><p>
<strong>index.html</strong></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;head&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;title&gt;</span>Mayx的博客<span class="nt">&lt;/title&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
<span class="nt">&lt;script&gt;</span>
<span class="c1">// 注册 Service Worker</span>
<span class="k">if</span> <span class="p">(</span><span class="dl">'</span><span class="s1">serviceWorker</span><span class="dl">'</span> <span class="k">in</span> <span class="nb">navigator</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="dl">'</span><span class="s1">/sw.js</span><span class="dl">'</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">registration</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Service Worker 注册成功:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">registration</span><span class="p">.</span><span class="nx">scope</span><span class="p">);</span>
<span class="c1">// 刷新网页</span>
<span class="nx">location</span><span class="p">.</span><span class="nx">reload</span><span class="p">();</span>
<span class="p">})</span>
<span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">error</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Service Worker 注册失败:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
<span class="nx">location</span><span class="o">=</span><span class="dl">"</span><span class="s2">https://mabbs.github.io</span><span class="dl">"</span><span class="p">;</span>
<span class="p">});</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">location</span><span class="o">=</span><span class="dl">"</span><span class="s2">https://mabbs.github.io</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;h1&gt;</span>Redirecting<span class="ni">&amp;hellip;</span><span class="nt">&lt;/h1&gt;</span>
<span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://mabbs.github.io"</span><span class="nt">&gt;</span>Click here if you are not redirected.<span class="nt">&lt;/a&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>
<p><strong>sw.js</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">TARGET_SITE</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">被反代的网站</span><span class="dl">'</span><span class="p">;</span> <span class="c1">//也可以用CORS代理</span>
<span class="nb">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">install</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="c1">// 强制立即激活新 Service Worker</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nx">skipWaiting</span><span class="p">());</span>
<span class="p">});</span>
<span class="nb">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">activate</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="c1">// 立即控制所有客户端</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nx">clients</span><span class="p">.</span><span class="nx">claim</span><span class="p">());</span>
<span class="p">});</span>
<span class="nb">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">fetch</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">new</span> <span class="nx">URL</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">).</span><span class="nx">origin</span> <span class="o">==</span> <span class="nb">self</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span><span class="nx">handleProxyRequest</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nx">handleProxyRequest</span><span class="p">(</span><span class="nx">request</span><span class="p">)</span> <span class="p">{</span>
<span class="k">try</span> <span class="p">{</span>
<span class="c1">// 构建目标 URL</span>
<span class="kd">const</span> <span class="nx">targetUrl</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URL</span><span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">proxyUrl</span> <span class="o">=</span> <span class="nx">TARGET_SITE</span> <span class="o">+</span> <span class="nx">targetUrl</span><span class="p">.</span><span class="nx">pathname</span> <span class="o">+</span> <span class="nx">targetUrl</span><span class="p">.</span><span class="nx">search</span><span class="p">;</span>
<span class="c1">// 创建新请求(复制原请求属性)</span>
<span class="kd">const</span> <span class="nx">proxyRequest</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Request</span><span class="p">(</span><span class="nx">proxyUrl</span><span class="p">,</span> <span class="p">{</span>
<span class="na">method</span><span class="p">:</span> <span class="nx">request</span><span class="p">.</span><span class="nx">method</span><span class="p">,</span>
<span class="c1">// headers: request.headers,</span>
<span class="c1">// body: request.body</span>
<span class="p">});</span>
<span class="c1">// 发送代理请求</span>
<span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">proxyRequest</span><span class="p">);</span>
<span class="c1">// 返回修改后的响应</span>
<span class="k">return</span> <span class="k">new</span> <span class="nx">Response</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">body</span><span class="p">,</span> <span class="p">{</span>
<span class="na">status</span><span class="p">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">status</span><span class="p">,</span>
<span class="na">statusText</span><span class="p">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">statusText</span><span class="p">,</span>
<span class="na">headers</span><span class="p">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">headers</span>
<span class="p">});</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Proxy error:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
<span class="k">return</span> <span class="k">new</span> <span class="nx">Response</span><span class="p">(</span><span class="dl">'</span><span class="s1">Proxy failed</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">status</span><span class="p">:</span> <span class="mi">500</span> <span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>最终的实际效果: <a href="https://mayx.nekoweb.org">https://mayx.nekoweb.org</a></p>
<h1 id="感想">
<a href="#感想"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 感想
</h1>
<p>虽然折腾了半天没能增强我博客的可靠性……但是体会到了现代浏览器的强大之处难怪前几年会提出ChromeOS和PWA之类的东西原来浏览器功能还是相当强大的用了Service Worker以后即使是纯前端也可以有和使用服务器一样的体验在过去的浏览器中要是想实现这样的功能……好像也不是不可能😂用AJAX加服务器使用伪静态策略其实是可以做到的……其实Service Worker的功能更多还是在离线时使用的我这个例子好像没体现它的优势😆。 </p><p>
但总的来说相比以前想要实现这种反代的功能代码还是更清晰也更简单了也许以后如果有机会我又有心思让博客在访客浏览器上离线运行那就可以体现Service Worker真正的优势了🤣。</p></main>
<small style="display: block">tags: <a rel="category tag" class="p-category" href="/search.html?keyword=%E6%B5%8F%E8%A7%88%E5%99%A8"><em>浏览器</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=Service%20Worker"><em>Service Worker</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=Worker"><em>Worker</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86"><em>反向代理</em></a> <span style="float: right;"><a href="https://gitlab.com/mayx/mayx.gitlab.io/tree/master/_posts/2025-08-01-sw-proxy.md">查看原始文件</a></span></small>
<h4 style="border-bottom: 1px solid #e5e5e5;margin: 2em 0 5px;">推荐文章</h4>
<p id="suggest-container">Loading...</p>
<script>
var suggest = $("#suggest-container");
$.get(BlogAPI + "/suggest?id=/2025/08/01/sw-proxy.html&update=" + lastUpdated.valueOf(), function (data) {
if (data.length) {
getSearchJSON(function (search) {
suggest.empty();
var searchMap = {};
for (var i = 0; i < search.length; i++) {
searchMap[search[i].url] = search[i];
}
var tooltip = $('<div class="content-tooltip"></div>').appendTo('body').hide();
for (var j = 0; j < data.length; j++) {
var item = searchMap[data[j].id];
if (item) {
var link = $('<a href="' + item.url + '">' + item.title + '</a>');
var contentPreview = item.content.substring(0, 100);
if (item.content.length > 100) {
contentPreview += "……";
}
link.hover(
function(e) {
tooltip.text($(this).data('content'))
.css({
top: e.pageY + 10,
left: e.pageX + 10
})
.show();
},
function() {
tooltip.hide();
}
).mousemove(function(e) {
tooltip.css({
top: e.pageY + 10,
left: e.pageX + 10
});
}).data('content', contentPreview);
suggest.append(link);
suggest.append(' - ' + item.date + '<br />');
}
}
});
} else {
suggest.html("暂无推荐文章……");
}
});
</script>
<br />
<div class="pagination">
<span class="prev">
<a href="/2025/07/24/screenshot.html">
上一篇使用Cloudflare制作自动更新的网站预览图
</a>
</span>
<br />
<span class="next">
<a href="/2025/08/10/tilde.html">
下一篇在Tilde社区的游玩体验
</a>
</span>
</div>
<!--[if !IE]> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="/assets/js/gitalk.min.js"></script>
<div id="gitalk-container"></div>
<script>
var gitalk = new Gitalk({
clientID: '36557aec4c3cb04f7ac6',
clientSecret: 'ac32993299751cb5a9ba81cf2b171cca65879cdb',
repo: 'mabbs.github.io',
owner: 'Mabbs',
admin: ['Mabbs'],
id: '/2025/08/01/sw-proxy', // Ensure uniqueness and length less than 50
distractionFreeMode: false, // Facebook-like distraction free mode
proxy: "https://cors-anywhere.mayx.eu.org/?https://github.com/login/oauth/access_token"
})
gitalk.render('gitalk-container')
</script>
<!-- <![endif]-->
</section>
<!--[if !IE]> -->
<div id="landlord" style="left:5px;bottom:0px;">
<div class="message" style="opacity:0"></div>
<canvas id="live2d" width="500" height="560" class="live2d"></canvas>
<div class="live_talk_input_body">
<form id="live_talk_input_form">
<div class="live_talk_input_name_body" >
<input type="checkbox" id="load_this" />
<input type="hidden" id="post_id" value="/2025/08/01/sw-proxy.html" />
<label for="load_this">
<span style="font-size: 11px; color: #fff;">&#160;想问这篇文章</span>
</label>
</div>
<div class="live_talk_input_text_body">
<input name="talk" type="text" class="live_talk_talk white_input" id="AIuserText" autocomplete="off" placeholder="要和我聊什么呀?" />
<button type="submit" class="live_talk_send_btn" id="talk_send">发送</button>
</div>
</form>
</div>
<input name="live_talk" id="live_talk" value="1" type="hidden" />
<div class="live_ico_box" style="display:none;">
<div class="live_ico_item type_info" id="showInfoBtn"></div>
<div class="live_ico_item type_talk" id="showTalkBtn"></div>
<div class="live_ico_item type_music" id="musicButton"></div>
<div class="live_ico_item type_youdu" id="youduButton"></div>
<div class="live_ico_item type_quit" id="hideButton"></div>
<input name="live_statu_val" id="live_statu_val" value="0" type="hidden" />
<audio src="" style="display:none;" id="live2d_bgm" data-bgm="0" preload="none"></audio>
<input id="duType" value="douqilai" type="hidden" />
</div>
</div>
<div id="open_live2d">召唤伊斯特瓦尔</div>
<!-- <![endif]-->
<footer>
<p>
<small>Made with ❤ by Mayx<br />Last updated at 2026-01-01 00:00:18<br /> 总字数614622 - 文章数178 - <a href="/atom.xml" >Atom</a> - <a href="/README.html" >About</a></small>
</p>
</footer>
</div>
<script src="/assets/js/scale.fix.js"></script>
<!--[if !IE]> -->
<script src="/assets/js/main_new.js"></script>
<script src="/Live2dHistoire/live2d/js/live2d.js"></script>
<script src="/Live2dHistoire/live2d/js/message.js"></script>
<!-- <![endif]-->
</body>
</html>