Workers strips off important headers on cached responses

I am facing a weird issue where Cloudflare Workers will strip off important headers, when serving a cached response. If I make two requests to the same URL, the first hit/response will return the headers needed, however, if I repeat the request, then the second response (which is most likely served from Cloudflare cache) does not contain the “Cors” headers.

Here is an example of two requests sent to the same route, right after each other:

mf at My-MacBook-Pro in ~                                                                                                                                                                                         16:18
> curl 'https://app.domain.com/wp-json/jwr/v1/search-products?page=1&currency=EUR&bypassRules=true&excludeIds=&prepend=false&filter_material=gold&filter_product_cat=anklets&filter_stone=amazonites,ambers&filter_min_price=9999&cacheKey=66&lang=en' \
-H 'authority: app.domain.com' \
-H 'pragma: no-cache' \
-H 'cache-control: no-cache' \
-H 'sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"' \
-H 'accept: application/json, text/plain, */*' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36' \
-H 'origin: http://192.168.39.177:3000' \
-H 'sec-fetch-site: cross-site' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-dest: empty' \
-H 'referer: http://192.168.39.177:3000/' \
-H 'accept-language: en-US,en;q=0.9,da;q=0.8' \
--compressed --verbose
*   Trying 104.26.5.95...
* TCP_NODELAY set
* Connected to app.domain.com (104.26.5.95) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-CHACHA20-POLY1305
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=CA; L=San Francisco; O=Cloudflare, Inc.; CN=sni.cloudflaressl.com
*  start date: Jul 19 00:00:00 2020 GMT
*  expire date: Jul 19 12:00:00 2021 GMT
*  subjectAltName: host "app.domain.com" matched cert's "*.domain.com"
*  issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fd84500d600)
> GET /wp-json/jwr/v1/search-products?page=1&currency=EUR&bypassRules=true&excludeIds=&prepend=false&filter_material=gold&filter_product_cat=anklets&filter_stone=amazonites,ambers&filter_min_price=9999&cacheKey=66&lang=en HTTP/2
> Host: app.domain.com
> Accept-Encoding: deflate, gzip
> authority: app.domain.com
> pragma: no-cache
> cache-control: no-cache
> sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"
> accept: application/json, text/plain, */*
> sec-ch-ua-mobile: ?0
> user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36
> origin: http://192.168.39.177:3000
> sec-fetch-site: cross-site
> sec-fetch-mode: cors
> sec-fetch-dest: empty
> referer: http://192.168.39.177:3000/
> accept-language: en-US,en;q=0.9,da;q=0.8
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
< HTTP/2 200
< date: Sat, 17 Apr 2021 14:18:42 GMT
< content-type: application/json; charset=UTF-8
< set-cookie: __cfduid=dcae662ce1d8a505cb94a4ac78b4ff2551618669121; expires=Mon, 17-May-21 14:18:41 GMT; path=/; domain=.domain.com; HttpOnly; SameSite=Lax; Secure
< x-powered-by: PHP/7.4.16
< access-control-allow-origin: http://192.168.39.177:3000
< access-control-allow-methods: POST, GET, OPTIONS, DELETE
< access-control-allow-credentials: true
< vary: Origin,User-Agent,Accept-Encoding
< x-uri: /wp-json/jwr/v1/search-products?page=1&currency=EUR&bypassRules=true&excludeIds=&prepend=false&filter_material=gold&filter_product_cat=anklets&filter_stone=amazonites,ambers&filter_min_price=9999&cacheKey=66&lang=en
< x-method: GET
< x-robots-tag: noindex
< link: <https://app.domain.com/wp-json/>; rel="https://api.w.org/"
< x-content-type-options: nosniff
< access-control-expose-headers: X-WP-Total, X-WP-TotalPages, Link
< access-control-allow-headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type
< allow: GET
< last-modified: Sat, 17 Apr 2021 14:18:42 GMT
< cache-control: public, max-age=0
< expires: Sat, 17 Apr 2021 14:18:42 GMT
< x-turbo-charged-by: LiteSpeed
< cf-cache-status: DYNAMIC
< cf-request-id: 0981cbc97c0000d891510f2000000001
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< report-to: {"max_age":604800,"group":"cf-nel","endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=jcv4HZ0YWTz2FT5LXpa0fx5da2zMSwJAYgEeUWbd95K%2FkjYZ3dyYsLA36WrCy27Ru63WMk%2FwvLVeXfLdV9Ic2QgAGXVuQ84%2FagLQk%2FOgFFXkDYOnjG6AfVk%3D"}]}
< nel: {"max_age":604800,"report_to":"cf-nel"}
< server: cloudflare
< cf-ray: 641648bbfd21d891-CPH
< content-encoding: gzip
< alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
<

mf at My-MacBook-Pro in ~                                                                                                                                                                                         16:18
> curl 'https://app.domain.com/wp-json/jwr/v1/search-products?page=1&currency=EUR&bypassRules=true&excludeIds=&prepend=false&filter_material=gold&filter_product_cat=anklets&filter_stone=amazonites,ambers&filter_min_price=9999&cacheKey=66&lang=en' \
-H 'authority: app.domain.com' \
-H 'pragma: no-cache' \
-H 'cache-control: no-cache' \
-H 'sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"' \
-H 'accept: application/json, text/plain, */*' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36' \
-H 'origin: http://192.168.39.177:3000' \
-H 'sec-fetch-site: cross-site' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-dest: empty' \
-H 'referer: http://192.168.39.177:3000/' \
-H 'accept-language: en-US,en;q=0.9,da;q=0.8' \
--compressed --verbose
*   Trying 104.26.5.95...
* TCP_NODELAY set
* Connected to app.domain.com (104.26.5.95) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-CHACHA20-POLY1305
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=CA; L=San Francisco; O=Cloudflare, Inc.; CN=sni.cloudflaressl.com
*  start date: Jul 19 00:00:00 2020 GMT
*  expire date: Jul 19 12:00:00 2021 GMT
*  subjectAltName: host "app.domain.com" matched cert's "*.domain.com"
*  issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7f8d47809200)
> GET /wp-json/jwr/v1/search-products?page=1&currency=EUR&bypassRules=true&excludeIds=&prepend=false&filter_material=gold&filter_product_cat=anklets&filter_stone=amazonites,ambers&filter_min_price=9999&cacheKey=66&lang=en HTTP/2
> Host: app.domain.com
> Accept-Encoding: deflate, gzip
> authority: app.domain.com
> pragma: no-cache
> cache-control: no-cache
> sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"
> accept: application/json, text/plain, */*
> sec-ch-ua-mobile: ?0
> user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36
> origin: http://192.168.39.177:3000
> sec-fetch-site: cross-site
> sec-fetch-mode: cors
> sec-fetch-dest: empty
> referer: http://192.168.39.177:3000/
> accept-language: en-US,en;q=0.9,da;q=0.8
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
< HTTP/2 200
< date: Sat, 17 Apr 2021 14:19:04 GMT
< content-type: text/html; charset=UTF-8
< set-cookie: __cfduid=d9ad5aa0686e002b05da4c76283b4dd031618669144; expires=Mon, 17-May-21 14:19:04 GMT; path=/; domain=.domain.com; HttpOnly; SameSite=Lax; Secure
< x-powered-by: PHP/7.4.16
< last-modified: Sat, 17 Apr 2021 14:18:42 GMT
< cache-control: public, max-age=0
< expires: Sat, 17 Apr 2021 14:19:04 GMT
< vary: Accept-Encoding,User-Agent,Accept-Encoding
< alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
< x-turbo-charged-by: LiteSpeed
< cf-cache-status: DYNAMIC
< cf-request-id: 0981cc22fc000010adc1a49000000001
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=W6Kp%2FdtenziTo1JIYMOk8ZxVJO%2BDf%2FT%2FCwDkHIgfZAjLHqhKxcGGFhhHCdtWZCKUUu%2BjVixXtwFzV%2BF8%2Bz8fLwMP8U3acj1VIvX3g3ZfxI4LnW%2BYkoW4EZM%3D"}],"group":"cf-nel","max_age":604800}
< nel: {"max_age":604800,"report_to":"cf-nel"}
< server: cloudflare
< cf-ray: 6416494b2af310ad-CPH
< content-encoding: gzip

I am unsure why this happens.

My proxy script is a very simple Cloudflare Worker script:

	const url = new URL(req.url)
	url.hostname = WordpressHostname;

	// fetch from remote
	const originalResponse = await fetch(url.toString(), {
		method: req.method || 'GET',
		body: req.body,
		headers: req.headers,
		cf: {
			cacheTtl,
			cacheEverything: true
		}
	});

	// prepare response
	const response = new Response(originalResponse.body, {
		headers: originalResponse.headers,
		status: originalResponse.status
	});

	return response;