From fec5c3b82b3914fbfb3b55918793bccd491329d0 Mon Sep 17 00:00:00 2001 From: ahaapple Date: Sat, 9 Nov 2024 18:40:03 +0800 Subject: [PATCH] Add anthropic-claude-api-proxy-403-forbidden blog --- ...thropic-claude-api-proxy-403-forbidden.mdx | 215 ++++++++++++++++++ ...thropic-claude-api-proxy-403-forbidden.mdx | 215 ++++++++++++++++++ 2 files changed, 430 insertions(+) create mode 100644 frontend/content/blog/en/anthropic-claude-api-proxy-403-forbidden.mdx create mode 100644 frontend/content/blog/zh/anthropic-claude-api-proxy-403-forbidden.mdx diff --git a/frontend/content/blog/en/anthropic-claude-api-proxy-403-forbidden.mdx b/frontend/content/blog/en/anthropic-claude-api-proxy-403-forbidden.mdx new file mode 100644 index 00000000..ad27ae47 --- /dev/null +++ b/frontend/content/blog/en/anthropic-claude-api-proxy-403-forbidden.mdx @@ -0,0 +1,215 @@ +--- +title: Use Anthropic Cloud API Proxy to Resolve the 403 Forbidden Issue +description: How to use the Anthropic Cloud API proxy to resolve the 403 forbidden issue for a Cloudflare Next.js on Pages application +image: /images/blog/blog-post-3.jpg +date: '2024-11-09' +--- + +## I Background + +Referencing the previous article [Migrate MemFree from Vercel to Cloudflare Next.js on Pages](https://www.memfree.me/blog/couldflare-next-on-page-edge), I have deployed [PageGen AI](https://pagegen.ai/) on Cloudflare Next.js on Pages. + +However, recently when I accessed Anthropic's claude-3-5-sonnet-20241022, I received a 403 forbidden exception. My user was accessing it from Singapore, and Anthropic's supported regions include Singapore, so I was a bit confused at first. + +After having [MemFree](https://www.memfree.me/) analyze the error logs, it was discovered that the crucial point is that Cloudflare ultimately sent the request to Anthropic via the edge node in Hong Kong, resulting in the 403 forbidden exception. + +```ts + responseHeaders: { + 'cf-cache-status': 'DYNAMIC', + 'cf-ray': '8df5c220856a5dfc-HKG', + connection: 'keep-alive', + 'content-type': 'application/json', + date: 'Fri, 08 Nov 2024 13:02:35 GMT', + server: 'cloudflare', + 'transfer-encoding': 'chunked', + vary: 'Accept-Encoding', + 'x-robots-tag': 'none' + }, + responseBody: '{\n' + + ' "error": {\n' + + ' "type": "forbidden",\n' + + ' "message": "Request not allowed"\n' + + ' }\n' + + '}', +``` + +## II Analysis + +The user request flow provided by MemFree is as follows: + +```ts +Your Next.js App (Singapore) -> Cloudflare Edge (Hong Kong) -> Anthropic API +``` + +This means: + +1. The request was indeed initiated from Singapore (confirmed by cf-ipcountry: SG). +2. However, Cloudflare routed the request to the edge node in Hong Kong for processing. +3. The Anthropic API saw the request source as Hong Kong, resulting in the request being banned. + +### 2.1 Why did a request initiated from Singapore go through the Hong Kong edge node? + +Although the user's request was initiated from Singapore, the reasons are as follows: + +- Cloudflare chose the Hong Kong edge node to process your request based on its own network optimization strategy. +- This is because Cloudflare may have deemed the routing through the Hong Kong node to access the Anthropic API as more optimal. + +The HKG in the CF-Ray ID indicates: + +- This is the location of the Cloudflare edge node processing the request. +- It is not the origin of the request or the location of the target server. + +If it were deployed on Vercel, Vercel supports limiting the regions in which edge APIs run, and we could simply configure it. However, Cloudflare does not support setting the operational region of Pages, although it has a smart routing strategy, which does not match my requirements. + +I hope that my entire Next.js application remains fully edge-based, only requiring requests to the Anthropic API to bypass the Cloudflare Hong Kong edge node. + +## III Abandoned Solutions + +### 1 Use Cloudflare Load Balancer + +1. Create a load balancer. +2. Specify Singapore as the preferred region. +3. Route API requests through this load balancer. + +This option requires Cloudflare's enterprise edition, so it was abandoned. + +### 2 Cloudflare Workers Middleware + +The middleware cannot resolve this issue because when Cloudflare Workers send requests to Anthropic, they may still be routed to the Hong Kong edge node, and the middleware cannot configure requests to bypass the Hong Kong edge node. + +### 3 Cloudflare Argo Smart Routing + +1. Enable Argo Smart Routing. +2. In the Dashboard: + +- Network -> Traffic +- Enable Argo Smart Routing. +- This will automatically optimize the routing path. + +However, this solution does not guarantee that 100% of requests from Singapore will not go through the Hong Kong edge node. + +### 4 Cloudflare DNS Settings + +```ts +const API_ENDPOINTS = { + SG: 'https://sg-api.your-domain.com', + DEFAULT: 'https://api.your-domain.com', +}; + +async function fetchAPI() { + const endpoint = userIsInSG ? API_ENDPOINTS.SG : API_ENDPOINTS.DEFAULT; +} +``` + +This does not meet my requirements and is not easy to scale. + +## IV Final Solution: Using Vercel's Edge API Proxy for Requests to Anthropic API + +As mentioned earlier, since Vercel's Edge API supports setting the operational region, we can configure Vercel's Edge API proxy for requests to the Anthropic API and disable the Hong Kong region. + +The code for the Anthropic API proxy is as follows, supporting both Stream and non-Stream requests: + +```ts +import { NextRequest } from 'next/server'; + +export const runtime = 'edge'; +export const preferredRegion = [ + 'arn1', + 'bom1', + 'cdg1', + 'cle1', + 'cpt1', + 'dub1', + 'fra1', + 'gru1', + 'hnd1', + 'iad1', + 'icn1', + 'kix1', + 'lhr1', + 'pdx1', + 'sfo1', + 'sin1', + 'syd1', +]; + +const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization, anthropic-version, x-api-key', +}; + +const ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages'; + +export async function OPTIONS() { + return new Response(null, { + status: 204, + headers: corsHeaders, + }); +} + +export async function POST(req: NextRequest) { + try { + const headers = new Headers(req.headers); + const body = await req.json(); + const anthropicResponse = await fetch(ANTHROPIC_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'anthropic-version': headers.get('anthropic-version') || '2023-06-01', + 'x-api-key': headers.get('x-api-key') || '', + }, + body: body ? JSON.stringify(body) : null, + }); + + const responseHeaders = new Headers(corsHeaders); + + if (anthropicResponse.headers.get('content-type')?.includes('text/event-stream')) { + responseHeaders.set('Content-Type', 'text/event-stream'); + responseHeaders.set('Cache-Control', 'no-cache'); + responseHeaders.set('Connection', 'keep-alive'); + + return new Response(anthropicResponse.body, { + status: anthropicResponse.status, + headers: responseHeaders, + }); + } + + const data = await anthropicResponse.json(); + if (!anthropicResponse.ok) { + return new Response(JSON.stringify(data), { + status: anthropicResponse.status, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + }); + } + + return new Response(JSON.stringify(data), { + status: anthropicResponse.status, + headers: responseHeaders, + }); + } catch (error) { + console.error('Proxy error:', error); + return new Response( + JSON.stringify({ + type: 'error', + error: { + type: 'internal_server_error', + message: 'Internal Server Error', + }, + }), + { + status: 500, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + }, + ); + } +} +``` + +We create the simplest next application using `npx create-next-app@latest --typescript`, then copy the above code into the API file and deploy it. This way, we can resolve the unexpected 403 forbidden issue with the Anthropic API. \ No newline at end of file diff --git a/frontend/content/blog/zh/anthropic-claude-api-proxy-403-forbidden.mdx b/frontend/content/blog/zh/anthropic-claude-api-proxy-403-forbidden.mdx new file mode 100644 index 00000000..875528e7 --- /dev/null +++ b/frontend/content/blog/zh/anthropic-claude-api-proxy-403-forbidden.mdx @@ -0,0 +1,215 @@ +--- +title: Use Anthropic cloude api proxy to reslove the 403 forbidden issue +description: How to anthropic cloude api proxy to reslove the 403 forbidden issue for a Cloudflare next-on-pages application +image: /images/blog/blog-post-3.jpg +date: '2024-11-09' +--- + +## 一 背景 + +参考上篇 [Migrate MemFree from Vercel to Cloudflare next-on-pages](https://www.memfree.me/blog/couldflare-next-on-page-edge), 我将 [PageGen AI](https://pagegen.ai/) 部署在了 Cloudflare next-on-pages 上。 + +但是最近当我访问 Anthropic 的 claude-3-5-sonnet-20241022 时,我得到了 403 forbidden 的异常,我的用户是从新加坡访问,Anthropic的支持的地区是包括新加坡的,所以我一开始有点困惑。 + +当我让 [MemFree](https://www.memfree.me/) 分析错误日志后,MemFree 发现了一个关键点,即 Cloudflare 最终是通过 HKG 的 edge 节点向 Anthropic 发起的请求,所以得到了 403 forbidden 的异常。 + +```ts + responseHeaders: { + 'cf-cache-status': 'DYNAMIC', + 'cf-ray': '8df5c220856a5dfc-HKG', + connection: 'keep-alive', + 'content-type': 'application/json', + date: 'Fri, 08 Nov 2024 13:02:35 GMT', + server: 'cloudflare', + 'transfer-encoding': 'chunked', + vary: 'Accept-Encoding', + 'x-robots-tag': 'none' + }, + responseBody: '{\n' + + ' "error": {\n' + + ' "type": "forbidden",\n' + + ' "message": "Request not allowed"\n' + + ' }\n' + + '}', +``` + +## 二 分析 + +MemFree 给出的用户请求流程如下: + +```ts +Your Next.js App (Singapore) -> Cloudflare Edge (Hong Kong) -> Anthropic API +``` + +也就是说: + +1. 用户的确是在新加坡发起的请求(通过 cf-ipcountry: SG 可以确认) +2. 但是Cloudflare 将请求路由到了香港的边缘节点处理 +3. Anthropic API 看到的请求来源是香港,所以禁止了请求 + +### 2.1 为什么新加坡发起的请求会经过香港的边缘节点 + +虽然用户的请求是从新加坡发起的,但是: + +- Cloudflare 根据自己的网络优化策略,选择了香港的边缘节点来处理您的请求 +- 这是因为 Cloudflare 可能认为通过香港节点访问 Anthropic API 的路由更优 + +CF-Ray ID 中的 HKG 表示: + +- 这是处理请求的 Cloudflare 边缘节点位置 +- 不是请求的发起地或目标服务器的位置 + +如果是在 Vercel 部署,Vercel 支持限制 edge API 运行的地区,我们简单配置下即可,但是 Cloudflare 并不支持设置 Page 运行的地区,虽然有一个 Smart的放置策略,但是和我的需求不符合。 + +我希望我的整个 Next js 应用依然是完全edge的,只是请求 Anthropic的一个 API 不经过 Cloudflare Hong Kong的 Edge 节点。 + +## 三 放弃的方案 + +### 1 使用 Cloudflare Load Balancer + +1. 创建一个负载均衡器 +2. 指定新加坡区域作为优先节点 +3. 将 API 请求路由通过这个负载均衡器 + +这个需要 Cloudflare的企业版,放弃了 + +### 2 Cloudflare Workers 中间件 + +中间件无法解决这个问题,因为当 Cloudflare Workers 向 Anthropic 发起请求时,还是有可能被路由到香港的 edge 节点,中间件 没法设置请求不走 香港的 edge 节点 + +### 3 Cloudflare Argo Smart Routing + +1. 启用 Argo Smart Routing +2. 在 Dashboard 中: + +- Network -> Traffic +- 启用 Argo Smart Routing +- 这将自动优化路由路径 + +但是这种方案没法保证百分之一百新加坡的请求不走香港的边缘节点 + +### 4 Cloudflare DNS 设置 + +```ts +const API_ENDPOINTS = { + SG: 'https://sg-api.your-domain.com', + DEFAULT: 'https://api.your-domain.com', +}; + +async function fetchAPI() { + const endpoint = userIsInSG ? API_ENDPOINTS.SG : API_ENDPOINTS.DEFAULT; +} +``` + +不满足我的需求,不易扩展 + +## 四 最终方案:利用 Vercel 的Edge API 代理对 Anthropic API的请求 + +前面提到,既然 Vercel的 Edge API 支持设置运行的地区,那么我们就可以让 Vercel 的 Edge API 代理 对 Anthropic API的请求,禁掉香港的region就可以。 + +Anthropic API 代理的代码如下,同时支持 Stream 和非 Stream的请求: + +```ts +import { NextRequest } from 'next/server'; + +export const runtime = 'edge'; +export const preferredRegion = [ + 'arn1', + 'bom1', + 'cdg1', + 'cle1', + 'cpt1', + 'dub1', + 'fra1', + 'gru1', + 'hnd1', + 'iad1', + 'icn1', + 'kix1', + 'lhr1', + 'pdx1', + 'sfo1', + 'sin1', + 'syd1', +]; + +const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization, anthropic-version, x-api-key', +}; + +const ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages'; + +export async function OPTIONS() { + return new Response(null, { + status: 204, + headers: corsHeaders, + }); +} + +export async function POST(req: NextRequest) { + try { + const headers = new Headers(req.headers); + const body = await req.json(); + const anthropicResponse = await fetch(ANTHROPIC_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'anthropic-version': headers.get('anthropic-version') || '2023-06-01', + 'x-api-key': headers.get('x-api-key') || '', + }, + body: body ? JSON.stringify(body) : null, + }); + + const responseHeaders = new Headers(corsHeaders); + + if (anthropicResponse.headers.get('content-type')?.includes('text/event-stream')) { + responseHeaders.set('Content-Type', 'text/event-stream'); + responseHeaders.set('Cache-Control', 'no-cache'); + responseHeaders.set('Connection', 'keep-alive'); + + return new Response(anthropicResponse.body, { + status: anthropicResponse.status, + headers: responseHeaders, + }); + } + + const data = await anthropicResponse.json(); + if (!anthropicResponse.ok) { + return new Response(JSON.stringify(data), { + status: anthropicResponse.status, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + }); + } + + return new Response(JSON.stringify(data), { + status: anthropicResponse.status, + headers: responseHeaders, + }); + } catch (error) { + console.error('Proxy error:', error); + return new Response( + JSON.stringify({ + type: 'error', + error: { + type: 'internal_server_error', + message: 'Internal Server Error', + }, + }), + { + status: 500, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + }, + ); + } +} +``` + +我们通过 `npx create-next-app@latest --typescript` 创建一个最简单的next应用,然后将上面的代码复制到API 文件,部署即可, 这样就可以解决非预期内的Anthropic API的403 forbidden 问题。