Using Lambda Function URL with Cloudflare (error 522)

I’m trying to use Cloudflare to proxy some parts of an application I’m running on AWS (mostly lambda and s3).
The S3 side works fine, but I’m not able to connect to Lambda using the function url endpoint. I’d like to save costs on provisioning api gateway or load balancers as it’s a small project.
My configuration on Cloudflare is Flexible and I have a CNAME entry for dev-api targeting the lambda function url host (with the orange cloud).

Curling directly the function url works fine:

 curl https://<REDACTED> -v

* Mark bundle as not supporting multiuse

< HTTP/1.1 200 OK

< Date: Wed, 01 Jun 2022 15:58:17 GMT

< Content-Type: application/json

< Content-Length: 25

< Connection: keep-alive

< x-amzn-RequestId: 701924ee-b067-4fec-90ef-f3dab6083a3b

< X-Amzn-Trace-Id: root=1-62978c99-345544be764ebee0583544c5;sampled=0


* Connection #0 to host <REDACTED> left intact

{ 'Message' : 'Success' }%

curling the dev-api CNAME (the one hitting Cloudflare) gives the dreaded 522 error.

curl -v                                                                                    
*   Trying
* Connected to ( port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=California; L=San Francisco; O=Cloudflare, Inc.;
*  start date: May 31 00:00:00 2022 GMT
*  expire date: May 30 23:59:59 2023 GMT
*  subjectAltName: host "" matched cert's "*"
*  issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* 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 0x14e811600)
> GET / HTTP/2
> Host:
> user-agent: curl/7.79.1
> accept: */*
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
< HTTP/2 522 
< date: Wed, 01 Jun 2022 16:00:47 GMT
< content-length: 0
< cache-control: no-store, no-cache
< cf-cache-status: DYNAMIC
< expect-ct: max-age=604800, report-uri=""
< report-to: {"endpoints":[{"url":"https:\/\/\/report\/v3?s=QqaD5o7t8TMfHIZbpcq4IKic%2BaK0YSFskZ%2Fm0tfDgUCh1BnbYs4arddOVhOsU%2BR60X%2FIcdtkuwfZBK%2BKTKbAndNelXlfSs6dXnrzKC5rZ83K5QFDaA8KRPNPoQ2ZD9C3mj8FZvsWbQ%3D%3D"}],"group":"cf-nel","max_age":604800}
< nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
< server: cloudflare
< cf-ray: 714929451e8e41d0-MRS
< alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
* Connection #0 to host left intact

I was reading through but I have no clue how could I check if TCP keep alive is working for lambda function urls.
Any clues? Thank you!

In that case I am afraid you have a security issue to begin with.

Hi @sandro, thank you for your security remarks but you completely ignored the question :face_exhaling:

Have you confirmed that the Lambda URL would accept your hostname, Cloudflare aside?

It won’t receive Host: <REDACTED> as a request header, it’ll get Host:

In regards to what sandro said, Flexible means it’ll request the Lambda over HTTP - aside from security concerns of using HTTP over the internet, that means the Lambda has to be happy to use HTTP.


I found two things about lambda function endpoints regarding your questions:

1/ Lambda responds with 403 to any request that doesn’t have Host: <REDACTED> Shouldn’t Cloudflare be using that host header in their requests?
I tried with the cloud in gray to
curl -H 'Host: <REDACTED>' -v --insecure actually works. But obviously you can’t tamper the Host on the browser or rewrite it with a Cloudflare rule. (Also the aws generated certificate won’t accept in the Cloudflare one). And also exposes the lambda urls, which I’d like to avoid for full DDoS/DoW protection.
Forgot to mention, I have CORS disabled in lambda but even when enabled it won’t accept calls from those domains in the Host header, so it seems the Host header has to be by all means.

2/ Lambda function url endpoints are HTTPS only. The flexible setup is because I also have public S3 bucket content served and changing it to Full makes is stop working. I’m trying to avoid using cloudfront, api-gateway or load balancers to make the costs minimal.


The Host header should represent the hostname that you accessed it with - in this case,

The Host request-header field specifies the Internet host and port number of the resource being requested, as obtained from the original URI given by the user or referring resource

This won’t work with Flexible. Whilst you really should move away from it entirely, you can setup a Page Rule to change it for a specific URL.


Alas, you still need to fix the Host header issue before that’ll change anything anyways.

1 Like

Tried with full, these are the results:

curl -H "Host: <REDACTED>" -v
< HTTP/2 403

< server: cloudflare

< date: Thu, 02 Jun 2022 21:30:06 GMT

< content-type: text/html

< content-length: 151

< cf-ray: 715349ce4e1c6a08-MAD



<head><title>403 Forbidden</title></head>


<center><h1>403 Forbidden</h1></center>




Cloudflare returns 403 as the host header seems tampered from his point of view.

curl -v  

* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
< HTTP/2 403 
< date: Thu, 02 Jun 2022 21:30:26 GMT
< content-type: application/json
< content-length: 16
< x-amzn-requestid: 93646b76-03a2-4519-9bd6-d41a8383ee8d
< x-amzn-errortype: AccessDeniedException
< cf-cache-status: DYNAMIC
< expect-ct: max-age=604800, report-uri=""
< report-to: {"endpoints":[{"url":"https:\/\/\/report\/v3?s=98L8u%2BOOinb10UDTiZkP%2FiHWiiYgFkNQDi6pGlDnldtjoZ64JOBO98zZsR%2BK6JaeAVD9oH7NfTH1nPZ3vcbLxEzMmG9zRY7Fkj9ls7%2Fyp4XTFsYB4nA8dXJIZ2UWXx6OKSgMudw5qQ%3D%3D"}],"group":"cf-nel","max_age":604800}
< nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
< server: cloudflare
< cf-ray: 71534a4c19cb69fc-MAD
< alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
* Connection #0 to host left intact

AWS returns 403 as the host header seems tampered from his point of view.

That bring us again to the previous question, why is Cloudflare making a request to a host without passing that host in the header? And how to correct it?

Like I said, Cloudflare’s behaviour is correct.

The Host header has no relation to your CNAME target - it should, and is, the original URL that the user accessed.

You need to setup Lambda, one way or another, to recognize that as a ‘custom’ or additional domain that should be answered on.

Alternatively, use Cloudflare Workers to fetch it on your behalf with the Lambda URL.

Admittedly, Workers are Cloudflare’s equivalent to Lambdas - so you could even replicate it within Workers depending what your Lambda does.

Cool, thank you for the insight. I’m afraid that setting is not available with lambda function urls, I’ll need to rethink my options.

Regarding the disable security rule, should it be like this? It doesn’t seem to work even after purging the cache and in developer mode.

Thanks a lot for your help KianNH!

Depends what you’re trying to disable - Disable Security is just a subset of features.

Is it relevant to this Lambda scenario? If not, probably best to open a new topic for the page rule.

Just for the sake of completeness, the rule works when using SSL → Flexible instead of Security.

Thank you again!

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.