Analysis¶
Generally, using Cloudflare Worker for edge computing is very suitable for reverse proxy. We can try to use Cloudflare Worker to mount a custom domain for Jike Yellow Page (hereinafter referred to as YP).
First, let's analyze the web page rendering process of YP:
- Load the webpage GET jike.city/xxxxx
- Load JS file according to HTML GET static.codefuture.top/_next/xxxxx.js
- Run JS to call the interface, extract the path from the current router, and call the interface to get data GET api.ruoguoapp.com/xxxx
- Render web page
Whether it's CDN or api interface, cross-domain checks (anti-hotlinking) will be performed, so all network requests must occur through the worker itself as a proxy.
After understanding this process, we can formulate a simple hijacking scheme:
- Use worker to fetch the original webpage.
If the current webpage path is not the configured username, issue a 302 to instruct the browser to redirect to the specific path, which facilitates subsequent js to extract parameters through the path.
- Replace all uses of static.codefuture.top in the webpage, hijacking all static file links to proxy yourself.
- When proxying the js file, replace api.ruguoapp.com in the js file with your own, hijacking all api requests.
Practice¶
Configure Domain and Worker¶
Writing the script¶
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request).catch(err=>console.error(err)));
})
// 替换为自己的 username
const username = "sorcererxw"
async function fetchAndApply(request) {
let url = new URL(request.url)
const sourceHost = url.host
// handle static
if(/^\/static/.test(url.pathname)) {
url.protocol = 'https:'
url.host = "static.codefuture.top"
url.pathname = url.pathname.replace(/\/static/,"")
const rsp = await fetch(url.href)
if(/\.js$/.test(url.pathname)) {
let text = await rsp.text()
text = text.replaceAll("api.ruguoapp.com", sourceHost+"/api")
text = text.replaceAll("static.codefuture.top", "/static")
const header = new Headers(rsp.headers)
return new Response(text, {
status: rsp.status,
headers: header,
})
}else {
return new Response(rsp.body, {
status: rsp.status,
headers: rsp.headers,
})
}
}
// handle api
if(/^\/api/.test(url.pathname)) {
url.protocol = 'https:'
url.pathname = url.pathname.replace(/\api/,"")
url.host = "api.ruguoapp.com"
const rsp = await fetch(url.href)
return new Response(rsp.body, {
status: rsp.status,
headers: rsp.headers,
})
}
// handle favicon
if(url.pathname==="/favicon") {
return new Response("",{
status: 404,
})
}
// handle page redirection
if(url.pathname!=("/"+username)) {
return new Response("",{
status: 302,
headers: {
Location: "/"+username
}
})
}
// fetch original html
const original_response = await fetch('https://jike.city/'+username)
const new_response_headers = new Headers(original_response.headers)
new_response_headers.set('access-control-allow-origin', '*')
new_response_headers.set('access-control-allow-credentials', true)
let original_text = null
const content_type = new_response_headers.get('content-type')
if (content_type.includes('text/html')) {
const text = await original_response.text()
original_text = text.replaceAll(`https://static.codefuture.top`, `/static`)
} else {
original_text = original_response.body
}
return new Response(original_text, {
status: original_response.status,
headers: new_response_headers,
})
}
Finally, let's try our custom domain https://yp.sorcererxw.com/ and enjoy the sense of accomplishment!
Summary¶
The above analysis is actually a general process of using Cloudflare Worker to implement reverse proxy. With the same process, we can set up custom domain names for the vast majority of websites. For example, the widely appreciated project Fruition, which configures custom domain names for Notion through Cloudflare. After carefully analyzing its source code, you can find that it also hijacks and injects a large amount of Notion's entire loading process to achieve a good reverse proxy effect.