在 Cloudflare 的「网站 - 规则 - 页面规则」中可以设置 URL 转发,免费默认可以使用 3 个规则,可以进行简单的通配符转换,如果这个能满足需求那最好了。

如下:将带 www 的网址 301 重定向到不带 www 的。

匹配 URL 转发 URL 重定向
www.dvel.me/* https://dvel.me/$1 301

但是很多时候需要更复杂的 URL 转发,比如个人博客瞎折腾,网站的后缀甚至域名来回改,无法仅仅通过通配符做到全部的 301 重定向,这时候还得用 Workers。

Update:批量重定向

今天打开 Cloudflare 一看,出了个「批量重定向 Beta」。。。

但是免费版限制了 5 个列表,每个列表 20 个,如果量小的话建议用用这个,量大的用 Workers。

创建一个 Worker

创建后选择「资源 - 快速编辑」,默认代码:

// 事件监听 fetch
addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request))
})

// 返回一个只包含"Hello world"的响应
async function handleRequest(request) {
  return new Response("Hello world")
}

点击「发送」即可测试:

Cloudflare Workers

301 一个 URL 试试

将上面代码改为:

addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  return Response.redirect("https://www.google.com", 301)
}

再点击「发送」进行测试:

Cloudflare Workers

再在「触发器 - 路由 - 添加路由」中添加一个 URL 进行测试 dvel.me/test

然后在浏览器中进行访问此网址,发现这个 URL 已经成功 301 到 Google 了。

大致的做法就是这样,批量重定向只需要将路由改为 dvel.me/*,再将需要跳转的新老 URL 在代码中处理即可。

官方示例

在上图点击右上角的「示例」,可以找到两个 Redirect 的示例

第一个示例将所有的请求重定向到一个 URL:

// Redirect all requests to one URL

const destinationURL = "https://example.com"
const statusCode = 301

async function handleRequest(request) {
  return Response.redirect(destinationURL, statusCode)
}

addEventListener("fetch", async event => {
  event.respondWith(handleRequest(event.request))
})

第二个示例是仅域名互换,保留后面的 path。即 foo.com/a?b=cbar.com/a?b=c

// Redirect requests from one domain to another

const base = "https://example.com"
const statusCode = 301

async function handleRequest(request) {
  const url = new URL(request.url)
  const { pathname, search } = url
  const destinationURL = base + pathname + search

  return Response.redirect(destinationURL, statusCode)
}

addEventListener("fetch", async event => {
  event.respondWith(handleRequest(event.request))
})

这两个完全没必要,因为用「页面规则」的「URL 转发」就可以完成了,除非你 3 个免费的规则都用完了。

手写更通用的转换

一个简单的示例:如果访问的地址和 old_url 撞上了,则返回 new_url

addEventListener("fetch", event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    let old_url = "https://dvel.xyz/post/1234/"
    let new_url = "https://dvel.me/post/ba-la-ba-la/"

    if (request.url === old_url) {
        return Response.redirect(new_url, 301)
    }
    
    return fetch(request)
}

完整版(同一个域名下跳转)

效果:只重定向当前域名下指定的路径。以后要是修改路径了就在这更新一下。

旧地址 新地址
dvel.me/post/1234/ dvel.me/post/ba-la-ba-la/

添加触发器的路由:dvel.me/*

addEventListener("fetch", event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    const arr = [
        "https://dvel.me/post/1234/ https://dvel.me/post/ba-la-ba-la/",
        // ...
    ]

    let old_url, new_url
    for (const u2 of arr) {
        [old_url, new_url] = u2.split(' ')
        if (request.url === old_url) {
            return Response.redirect(new_url, 301)
        }
    }

    return fetch(request)
}

完整版(域名 A 跳转域名 B)

效果:默认只转换域名,并保留后面的 path 和 search;后缀和域名都变了的,需要一个一个写进数组。

旧地址 新地址
dvel.xyz/post/1234/ dvel.me/post/ba-la-ba-la/

添加触发器的路由:dvel.xyz/*

addEventListener("fetch", event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    const arr = [
        "https://dvel.xyz/post/1234/ https://dvel.me/post/ba-la-ba-la/",
        // ...
    ]

    let old_url, new_url
    for (const u2 of arr) {
        [old_url, new_url] = u2.split(' ')
        if (request.url === old_url) {
            return Response.redirect(new_url, 301)
        }
    }

    const base = "https://dvel.me"
    const url = new URL(request.url)
    const { pathname, search } = url
    const destinationURL = base + pathname + search
    return Response.redirect(destinationURL, 301)
}