diff --git a/.changeset/new-beers-worry.md b/.changeset/new-beers-worry.md new file mode 100644 index 000000000..0f8428e6c --- /dev/null +++ b/.changeset/new-beers-worry.md @@ -0,0 +1,9 @@ +--- +"@opennextjs/aws": patch +--- + +fix: remove `cf-connecting-ip` headers from external override requests + +this change removes `cf-connecting-ip` headers from requests being sent to +external urls during rewrites, this allows such overrides, when run inside a +Cloudflare worker to rewrite to urls also hosted on Cloudflare diff --git a/packages/open-next/src/overrides/proxyExternalRequest/fetch.ts b/packages/open-next/src/overrides/proxyExternalRequest/fetch.ts index c10234dab..275ff67bb 100644 --- a/packages/open-next/src/overrides/proxyExternalRequest/fetch.ts +++ b/packages/open-next/src/overrides/proxyExternalRequest/fetch.ts @@ -5,7 +5,14 @@ const fetchProxy: ProxyExternalRequest = { name: "fetch-proxy", // @ts-ignore proxy: async (internalEvent) => { - const { url, headers, method, body } = internalEvent; + const { url, headers: eventHeaders, method, body } = internalEvent; + + const headers = Object.fromEntries( + Object.entries(eventHeaders).filter( + ([key]) => key.toLowerCase() !== "cf-connecting-ip", + ), + ); + const response = await fetch(url, { method, headers, diff --git a/packages/tests-unit/tests/overrides/proxyExternalRequest/fetch.test.ts b/packages/tests-unit/tests/overrides/proxyExternalRequest/fetch.test.ts new file mode 100644 index 000000000..d529bc557 --- /dev/null +++ b/packages/tests-unit/tests/overrides/proxyExternalRequest/fetch.test.ts @@ -0,0 +1,39 @@ +import fetchProxy from "@opennextjs/aws/overrides/proxyExternalRequest/fetch.js"; +import { vi } from "vitest"; + +describe("proxyExternalRequest/fetch", () => { + // Note: if the url is hosted on the Cloudflare network we want to make sure that a `cf-connecting-ip` header is not being sent as that causes a DNS error + // (see: https://developers.cloudflare.com/support/troubleshooting/cloudflare-errors/troubleshooting-cloudflare-1xxx-errors/#error-1000-dns-points-to-prohibited-ip) + it("the proxy should remove any cf-connecting-ip headers (with any casing) before passing it to fetch", async () => { + const fetchMock = vi.fn(async () => new Response()); + globalThis.fetch = fetchMock; + + const { proxy } = fetchProxy; + + await proxy({ + headers: { + "header-1": "valid header 1", + "header-2": "valid header 2", + "cf-connecting-ip": "forbidden header 1", + "header-3": "valid header 3", + "CF-Connecting-IP": "forbidden header 2", + "CF-CONNECTING-IP": "forbidden header 3", + "header-4": "valid header 4", + }, + }); + + expect(fetchMock.mock.calls.length).toEqual(1); + + const headersPassedToFetch = Object.keys( + fetchMock.mock.calls[0][1]?.headers ?? {}, + ); + + expect(headersPassedToFetch).toContain("header-1"); + expect(headersPassedToFetch).toContain("header-2"); + expect(headersPassedToFetch).not.toContain("cf-connecting-ip"); + expect(headersPassedToFetch).toContain("header-3"); + expect(headersPassedToFetch).not.toContain("CF-Connecting-IP"); + expect(headersPassedToFetch).not.toContain("CF-CONNECTING-IP"); + expect(headersPassedToFetch).toContain("header-4"); + }); +});