VPS/云主机等
优惠信息分享~

利用Cloudflare Workers解决打开wordpress出现429 Too Many Requests的问题

wordpress不管是中国站点,还是国外主站,都2020年了,打开还是会提示“429 Too Many Requests”,看来是不准备解决了。

前面的文章讲了利用Cloudflare Workers来解决比如说谷歌不能访问的这些问题,那我们也可以利用cf workers来解决wordpress出现429 Too Many Requests的问题,说白了就是“饭袋”wordpress站点。了解本文之前,你得先看看之前的相关文章:

[mark_e]

[/mark_e]

1、前言

你首先需要申请好cloudflare账号,之后,我们申请好worker,使用免费版本足够了。具体的申请教程可以看上面推荐的文章,这里就不多做赘述了。

项目:https://github.com/EtherDream/jsproxy

2、放置代码

在Worker编辑页面左边粘贴如下的代码,具体下:[reply]

'use strict'

/**
 * static files (404.html, sw.js, conf.js)
 */
const ASSET_URL = 'https://etherdream.github.io/jsproxy'

const JS_VER = 10
const MAX_RETRY = 1

/** @type {RequestInit} */
const PREFLIGHT_INIT = {
  status: 204,
  headers: new Headers({
    'access-control-allow-origin': '*',
    'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS',
    'access-control-max-age': '1728000',
  }),
}

/**
 * @param {any} body
 * @param {number} status
 * @param {Object<string, string>} headers
 */
function makeRes(body, status = 200, headers = {}) {
  headers['--ver'] = JS_VER
  headers['access-control-allow-origin'] = '*'
  return new Response(body, {status, headers})
}

/**
 * @param {string} urlStr 
 */
function newUrl(urlStr) {
  try {
    return new URL(urlStr)
  } catch (err) {
    return null
  }
}

addEventListener('fetch', e => {
  const ret = fetchHandler(e)
    .catch(err => makeRes('cfworker error:\n' + err.stack, 502))
  e.respondWith(ret)
})

/**
 * @param {FetchEvent} e 
 */
async function fetchHandler(e) {
  const req = e.request
  const urlStr = req.url
  const urlObj = new URL(urlStr)
  const path = urlObj.href.substr(urlObj.origin.length)

  if (urlObj.protocol === 'http:') {
    urlObj.protocol = 'https:'
    return makeRes('', 301, {
      'strict-transport-security': 'max-age=99999999; includeSubDomains; preload',
      'location': urlObj.href,
    })
  }

  if (path.startsWith('/http/')) {
    return httpHandler(req, path.substr(6))
  }

  switch (path) {
  case '/http':
    return makeRes('请更新 cfworker 到最新版本!')
  case '/ws':
    return makeRes('not support', 400)
  case '/works':
    return makeRes('it works')
  default:
    // static files
    return fetch(ASSET_URL + path)
  }
}

/**
 * @param {Request} req
 * @param {string} pathname
 */
function httpHandler(req, pathname) {
  const reqHdrRaw = req.headers
  if (reqHdrRaw.has('x-jsproxy')) {
    return Response.error()
  }

  // preflight
  if (req.method === 'OPTIONS' &&
      reqHdrRaw.has('access-control-request-headers')
  ) {
    return new Response(null, PREFLIGHT_INIT)
  }

  let acehOld = false
  let rawSvr = ''
  let rawLen = ''
  let rawEtag = ''

  const reqHdrNew = new Headers(reqHdrRaw)
  reqHdrNew.set('x-jsproxy', '1')

  // 此处逻辑和 http-dec-req-hdr.lua 大致相同
  // https://github.com/EtherDream/jsproxy/blob/master/lua/http-dec-req-hdr.lua
  const refer = reqHdrNew.get('referer')
  const query = refer.substr(refer.indexOf('?') + 1)
  if (!query) {
    return makeRes('missing params', 403)
  }
  const param = new URLSearchParams(query)

  for (const [k, v] of Object.entries(param)) {
    if (k.substr(0, 2) === '--') {
      // 系统信息
      switch (k.substr(2)) {
      case 'aceh':
        acehOld = true
        break
      case 'raw-info':
        [rawSvr, rawLen, rawEtag] = v.split('|')
        break
      }
    } else {
      // 还原 HTTP 请求头
      if (v) {
        reqHdrNew.set(k, v)
      } else {
        reqHdrNew.delete(k)
      }
    }
  }
  if (!param.has('referer')) {
    reqHdrNew.delete('referer')
  }

  // cfworker 会把路径中的 `//` 合并成 `/`
  const urlStr = pathname.replace(/^(https?):\/+/, '$1://')
  const urlObj = newUrl(urlStr)
  if (!urlObj) {
    return makeRes('invalid proxy url: ' + urlStr, 403)
  }

  /** @type {RequestInit} */
  const reqInit = {
    method: req.method,
    headers: reqHdrNew,
    redirect: 'manual',
  }
  if (req.method === 'POST') {
    reqInit.body = req.body
  }
  return proxy(urlObj, reqInit, acehOld, rawLen, 0)
}

/**
 * 
 * @param {URL} urlObj 
 * @param {RequestInit} reqInit 
 * @param {number} retryTimes 
 */
async function proxy(urlObj, reqInit, acehOld, rawLen, retryTimes) {
  const res = await fetch(urlObj.href, reqInit)
  const resHdrOld = res.headers
  const resHdrNew = new Headers(resHdrOld)

  let expose = '*'

  for (const [k, v] of resHdrOld.entries()) {
    if (k === 'access-control-allow-origin' ||
        k === 'access-control-expose-headers' ||
        k === 'location' ||
        k === 'set-cookie'
    ) {
      const x = '--' + k
      resHdrNew.set(x, v)
      if (acehOld) {
        expose = expose + ',' + x
      }
      resHdrNew.delete(k)
    }
    else if (acehOld &&
      k !== 'cache-control' &&
      k !== 'content-language' &&
      k !== 'content-type' &&
      k !== 'expires' &&
      k !== 'last-modified' &&
      k !== 'pragma'
    ) {
      expose = expose + ',' + k
    }
  }

  if (acehOld) {
    expose = expose + ',--s'
    resHdrNew.set('--t', '1')
  }

  // verify
  if (rawLen) {
    const newLen = resHdrOld.get('content-length') || ''
    const badLen = (rawLen !== newLen)

    if (badLen) {
      if (retryTimes < MAX_RETRY) {
        urlObj = await parseYtVideoRedir(urlObj, newLen, res)
        if (urlObj) {
          return proxy(urlObj, reqInit, acehOld, rawLen, retryTimes + 1)
        }
      }
      return makeRes(res.body, 400, {
        '--error': `bad len: ${newLen}, except: ${rawLen}`,
        'access-control-expose-headers': '--error',
      })
    }

    if (retryTimes > 1) {
      resHdrNew.set('--retry', retryTimes)
    }
  }

  let status = res.status

  resHdrNew.set('access-control-expose-headers', expose)
  resHdrNew.set('access-control-allow-origin', '*')
  resHdrNew.set('--s', status)
  resHdrNew.set('--ver', JS_VER)

  resHdrNew.delete('content-security-policy')
  resHdrNew.delete('content-security-policy-report-only')
  resHdrNew.delete('clear-site-data')

  if (status === 301 ||
      status === 302 ||
      status === 303 ||
      status === 307 ||
      status === 308
  ) {
    status = status + 10
  }

  return new Response(res.body, {
    status,
    headers: resHdrNew,
  })
}

/**
 * @param {URL} urlObj 
 */
function isYtUrl(urlObj) {
  return (
    urlObj.host.endsWith('.googlevideo.com') &&
    urlObj.pathname.startsWith('/videoplayback')
  )
}

/**
 * @param {URL} urlObj 
 * @param {number} newLen 
 * @param {Response} res 
 */
async function parseYtVideoRedir(urlObj, newLen, res) {
  if (newLen > 2000) {
    return null
  }
  if (!isYtUrl(urlObj)) {
    return null
  }
  try {
    const data = await res.text()
    urlObj = new URL(data)
  } catch (err) {
    return null
  }
  if (!isYtUrl(urlObj)) {
    return null
  }
  return urlObj
}

[/reply]

保存之后的效果如下:

3、设置域名

必须要设置一个域名,在设置域名访问之前,请先把自己的 域名托管到cloudflare。具体的设置看这篇文章:

[mark_e]

Cloudflare Workers简单设置来访问Google – 谷歌镜像站

[/mark_e]

开头已经说明了,这里再次强调一下。

5、使用

上面的步骤都做好之后,我们现在就可以使用这个“袋里”来打开wordpress站点了,如图:

输入wordpress的网址之后,我们点击“Go”即可打开wordpress官网,如图:

在这个页面点击右上角的“Get WordPress”即可进入下载页面,然后点击“Download WordPress 5.3.2”就可以愉快下载程序了,如图:

当然,利用这个“袋里”你也可以顺利的打开wordpress中国站点。

6、总结

这个方法可以让你顺利浏览wordpress站点,下载需要的插件或者wordpress程序等。但是,还是不能后台更新,需要你手动更新。

有条件的可以自己弄一个富强,一切问题都可以解决。但是上面介绍的方法几乎是无成本,所以还是可以折腾下的。

赞(0)
未经允许不得转载:大鸟笔记 » 利用Cloudflare Workers解决打开wordpress出现429 Too Many Requests的问题

评论 15

  1. #-29

    真尴尬,常年国外主机,家里路由绕过大陆。一度不知道429是个什么鬼

    猫叔5年前 (2020-02-24)回复
    • 额,问题很严重,到现在也打不开wordpress。

      大鸟5年前 (2020-02-24)回复
  2. #-28

    我以为可以直接服务器自动更新呢。

    奶爸建网站笔记5年前 (2020-02-25)回复
  3. #-27

    心累的更新

    呆窝云5年前 (2020-03-02)回复
  4. #-26

    这个好,不知道速度如何!

    缙哥哥5年前 (2020-04-03)回复
  5. #-25

    这个方法好

    小小小鸟5年前 (2020-04-10)回复
  6. #-24

    学习一下 谢谢分享

    ray5年前 (2020-04-28)回复
  7. #-23

    妙极了,妙极了

    今天也在睡觉的咸鱼君4年前 (2020-07-27)回复
  8. #-22

    看看 解决方案

    paul4年前 (2021-03-15)回复
  9. #-21

    看看隐藏内容 学习下解决方案

    paul4年前 (2021-03-15)回复
  10. #-20

    来学习429解决方案

    paul4年前 (2021-03-15)回复
  11. #-19

    厉害呀大佬6666

    蜡笔小心4年前 (2021-04-22)回复
  12. #-18

    现在已经好了,以前抽风了好一阵429

    云点3年前 (2021-05-28)回复
  13. #-17

    学到了 赞

    小武2年前 (2022-05-08)回复
  14. #-16

    真尴尬,常年国外主机,家里路由绕过大陆。一度不知道429是个什么鬼

    小武2年前 (2022-05-08)回复