Skip to content

Instantly share code, notes, and snippets.

@wyf9
Last active November 30, 2025 11:16
Show Gist options
  • Select an option

  • Save wyf9/c791132274cd121c57d1b7d2cdc50fe5 to your computer and use it in GitHub Desktop.

Select an option

Save wyf9/c791132274cd121c57d1b7d2cdc50fe5 to your computer and use it in GitHub Desktop.
PyPI Mirror
// Enhanced PyPI Mirror Worker
// https://gist.github.com/wyf9/c791132274cd121c57d1b7d2cdc50fe5
// 添加屏蔽:登录/注册/账户/安全路径,防止 spam 检测
// 基于 https://github.com/ccbikai/cloudflare-pypi-mirror
const PyPI = 'https://pypi.org';
const PyPI_FILES = 'https://files.pythonhosted.org';
// 敏感路径屏蔽列表(PyPI 认证/账户相关,防止滥用)
const BLOCKED_PATHS = [
'/account/', // 账户根(包括 /account/login/, /account/register/, /account/profile/ 等)
'/account/login/',
'/account/register/',
'/account/reset-password/',
'/account/confirm/',
'/account/tokens/',
'/users/', // 用户搜索
'/help/', // 帮助页(子路径可能触发报告)
'/security/', // 安全报告
'/legacy/' // 旧版 API,易被爬虫滥用
];
export default {
async fetch(request) {
const url = new URL(request.url);
// 步骤1: 屏蔽敏感路径 - 如果匹配,直接返回 403
for (const blockedPath of BLOCKED_PATHS) {
if (url.pathname.startsWith(blockedPath)) {
console.log(`[BLOCKED] ${url.pathname} - Anti Spam`);
return new Response('Forbidden: This path is not supported by the mirror.', {
status: 403,
headers: {
'Content-Type': 'text/plain',
'X-Robots-Tag': 'noindex, nofollow, noarchive'
}
});
}
}
// 步骤2: 代理 /pypi/ 路径(包索引,如 /pypi/simple/)
if (url.pathname.startsWith('/pypi/')) {
const pypiPath = url.pathname.replace('/pypi', '');
const pypiUrl = `${PyPI}${pypiPath}`;
const response = await fetch(pypiUrl, request);
let body = await response.text();
// 替换文件下载链接为 mirror 路径
body = body.replace(new RegExp(PyPI_FILES, 'g'), `${url.origin}/files`);
// 优化头部:删除 ETag 避免跨域缓存问题
const headers = new Headers(response.headers);
headers.delete('ETag');
headers.set('Cache-Control', 'public, max-age=3600'); // 加 1h 缓存,加速重复访问
headers.set('X-Robots-Tag', 'noindex, nofollow, noarchive')
return new Response(body, {
status: response.status,
headers,
});
}
// 步骤3: 代理 /files/ 路径(实际包文件下载)
if (url.pathname.startsWith('/files/')) {
const filePath = url.pathname.replace('/files', '');
const fileUrl = `${PyPI_FILES}${filePath}`;
const response = await fetch(fileUrl, request);
// 优化:加缓存头部,提高下载速度
const headers = new Headers(response.headers);
headers.set('Cache-Control', 'public, max-age=86400'); // 1d 缓存
headers.set('X-Robots-Tag', 'noindex, nofollow, noarchive')
return new Response(response.body, {
status: response.status,
headers
});
}
// 默认:返回 mirror 状态页
return new Response(`
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PyPI Mirror</title>
<!-- 防止搜索引擎收录 -->
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet" />
<meta name="googlebot" content="noindex, nofollow" />
<meta name="baiduspider" content="noindex, nofollow" />
<meta name="bingbot" content="noindex, nofollow" />
<link rel="canonical" href="https://pypi-mirror.siiway.top/" />
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
:root {
--bg: #ffffff;
--text: #1f2328;
--primary: #2563eb;
--border: #e5e7eb;
--card: #f9fafb;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0d1117;
--text: #c9d1d9;
--primary: #58a6ff;
--border: #30363d;
--card: #161b22;
}
}
* { margin:0; padding:0; box-sizing:border-box; }
body {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
padding: 2rem 1rem;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
max-width: 720px;
width: 100%;
background: var(--card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 3rem 2.5rem;
box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04);
text-align: center;
}
h1 {
font-size: 2.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
background: linear-gradient(90deg, #2563eb, #7c3aed);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.badge {
display: inline-block;
background: #10b981;
color: white;
font-size: 0.75rem;
font-weight: 600;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
margin-bottom: 1.5rem;
}
.command {
background: #000000;
color: #ffffff;
padding: 1rem 1.5rem;
border-radius: 12px;
font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
font-size: 1rem;
margin: 1.5rem 0;
text-align: left;
overflow-x: auto;
border: 1px solid var(--border);
}
.command span {
color: #60a5fa;
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin: 2rem 0;
text-align: left;
}
.feature {
background: var(--bg);
padding: 1rem;
border-radius: 12px;
border: 1px solid var(--border);
}
.feature h3 {
color: var(--primary);
margin-bottom: 0.5rem;
font-size: 1rem;
}
footer {
margin-top: 3rem;
font-size: 0.875rem;
color: #8b949e;
}
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<div class="container">
<h1>PyPI Mirror</h1>
<div class="badge">全球高速 · 永久可用</div>
<p>基于 Cloudflare Worker 构建的 PyPI 镜像 (已屏蔽登录/注册等敏感路径)</p>
<div class="command">
pip install <span>requests</span> -i <span>https://${url.hostname}/pypi/simple/</span> --trusted-host <span>pypi-mirror.siiway.top</span>
</div>
<p>或者配置为默认源(推荐):</p>
<div class="command">
pip config set global.index-url <span>https://${url.hostname}/pypi/simple/</span><br>
pip config set global.trusted-host <span>${url.hostname}</span>
</div>
<div class="features">
<div class="feature">
<h3>极速下载</h3>
<p>Cloudflare 全球边缘缓存 (除中国大陆)</p>
</div>
<div class="feature">
<h3>防封设计</h3>
<p>已屏蔽登录、注册、账户管理等路径</p>
</div>
<div class="feature">
<h3>无限使用</h3>
<p>不设置用量限制,随便下载</p>
</div>
</div>
<footer>
查看源码: <a href="https://gist.github.com/wyf9/c791132274cd121c57d1b7d2cdc50fe5" target="_blank">Gist</a> ·
原作者 <a href="https://github.com/ccbikai/cloudflare-pypi-mirror" target="_blank">Repo</a>
</footer>
</div>
</body>
</html>
`, {
status: 200,
headers: {
'Content-Type': 'text/html; charset=utf-8',
'Cache-Control': 'public, max-age=3600',
'X-Robots-Tag': 'noindex, nofollow, noarchive'
}
});
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment