Different hostname with same origin in Workers?


#1

Hello,

Can Cloudflare workers overwrite the url hostname but use the original origin server IP address?

url.hostname = “example.com
return fetch(url, request)

Unfortunately the above example will not connect to the original origin server.


#2

Hi!

So, the change in hostname is allowed only when the other origin is in the user’s account, it’s orange-clouded and using the tag resolveOverride. If you change hostname it will connect to that hostname’s IP, otherwise you could try creating a brand new request, but it isn’t guaranteed. You should probably ask @KentonVarda.


#3

Thanks @matteo I double checked this and it appears resolveOverride uses the same hostname with a different origin. We are trying to use the same origin with a different hostname.


#4

Well, no: resolveOverride uses the same hostname and a different origin for the user and a different hostname and different origin for the domain owner. You can of course use the same origin if both hostnames point to the same origin.


#5

Thank you @matteo I think I understand what you are saying now.

We can use both url.hostname and resolveOverride together to change the hostname and keep the request going to the origin server?


#6

I believe so, it may not be needed to change the hostname at all (resolveOverride may change it), but putting both wouldn’t hurt.


#7

Just did some testing and unfortunately it did not work.

Example:

url.hostname = “example-hostname.tld”
return fetch(url, request, { cf: { resolveOverride: ‘example-origin-server.tld’ } })

Cloudflare connects to the hostname (example-hostname.tld) instead of the origin server (example-origin-server.tld).

What we want the Worker to do is use a different hostname when cloudflare is communicating with the host origin server. Similar to the “-H” option in curl:

curl -H ‘Host: example-hostname.tld’ http://127.0.0.1/


#8

In the resolveOverride you need to put the same hostname as in the change. Read the docs it’s explained well, it’s why I told you that you need to have it under Cloudflare (same account)…

What you are asking is otherwise impossible if you don’t control that domain, due to security issues!


#9

Perhaps there is another way to do this. Using url.hostname the domain does not need to be under Cloudflare. The cloudflare blog gave this example:

url.hostname = “example-bucket.storage.googleapis.com

What we want to do is send the request to a different IP from the hostname. This can be done with curl using “-H” or by editing the hosts file. Does anyone know a way to do this in Cloudflare Workers?


#10

Redirects the request to an alternate origin server. You can use this, for example, to implement load balancing across several origins.
You can achieve a similar effect by simply changing the request URL.
[…]
However, there is an important difference: If you use resolveOverride to change the origin, then the request will be sent with a Host header matching the original URL. Often, your origin servers will all expect the Host header to specify your web site’s main hostname, not the hostname of the specific replica. This is where resolveOverride is needed.
For security reasons, resolveOverride is only honored when both the URL hostname and the resolveOverride hostname are orange-cloud hosts within your own zone. Otherwise, the setting is ignored. Note that CNAME hosts are allowed. So, if you want to resolve to a hostname that is under a different domain, first declare a CNAME record within your own zone’s DNS mapping to the external hostname, then set resolveOverride to point to that CNAME record.

Read the paragraph, when you connect to a different origin it will use your original Host header. This is partly what you want, from what you told me. The problem is that you cannot easily do an HTTPS connection to an IP, the only perfect solution to what you are describing, which works only if it is HTTP (which I would not recommend, especially since there is a really easy solution that allows HTTPS), would be to use as hostname the IP of the origin.

// Only if HTTP, not verified
var newURL = new URL(request.url)
newURL.hostname = “8.8.8.8” // your origin IP
newURL.protocol = 'http:'

return fetch(newURL, request)

But since you need to do a sort of load balancing with one single origin, the best option is that you put the origin IP on a subdomain under your domain and then point to it via the request. I sincerely don’t know if you can use the same Host header (per the security reasons explained above), it could work, nut don’t believe so, if you select the new hostname in the URL and put the original address in the resolveOverride. If this works it would be like this:

// For HTTP(S), not verified
var newURL = new URL(request.url)
newURL.hostname = “origin.domain.tld”

return fetch(newURL, request, { cf: { resolveOverride: 'domain.tld' } })

The ideal and certainly working solution would be:

// For HTTP(S), should really work
var newURL = new URL(request.url)
newURL.hostname = “origin.domain.tld”

return fetch(newURL, request, { cf: { resolveOverride: 'origin.domain.tld' } })

In this case you would put an Origin certificate of origin.domain.tld on the origin and make it reply on that domain (instead of domain.tld). You can’t change IP of a DNS record in the Worker, it’s for security reason as almost all the limitations are.


#11

Hi @ns1,

Unfortunately, we cannot support what you’re asking for because it would create a security problem. Many Cloudflare users configure their origin servers to accept traffic only from Cloudflare’s edge IPs, in order to ensure that Cloudflare’s security protections have applied to all requests. However, an attacker could create a Cloudflare account and configure their origin server IP to be the same as some victim’s origin server. Thus, requests to the attacker’s zone will be passed on to the victim’s origin from Cloudflare’s IP addresses, but without the victim’s Cloudflare settings having been applied. This is OK only because of the Host header – usually, origin servers will simply return 404 for any request whose Host header is not what the origin server expects. For origin servers that are whitelisting Cloudflare IP addresses, they must also check the Host header in this way for security.

Because of this widely-deployed security pattern, we cannot allow you to set the Host header to a third-party hostname unless you are actually sending the request to that host’s publicly resolved IP address (which is what happens when you change the host in the URL passed to fetch()).

Here are some alternative implementation strategies you could try here:

  1. If you control the origin server, you can make it accept a custom header like X-Alt-Host which you set in your worker, and which overrides the Host header.
  2. If you control the alternate hostname you want to set, then you could configure DNS for that hostname to point to the same origin server.

#12

Just for my clarification @KentonVarda,

my option would not work at all? Even if he put the alternate entry in his DNS and did a resolveOverride to it?

Didn’t think about the custom header!


#13

Thank you @matteo and @KentonVarda, I appreciate your help.

The new hostname is subdomain we control however we put a page rule to redirect all traffic. When we fetch the new hostname with workers the page rule forces a redirect. Is there anyway to stop the page rule when workers is fetching the hostname or connect directly to our origin server?


#14

I don’t believe so, but put it in a random sub-sub-domain… It won’t get exposed and it’s difficult to find.


#15

@ns1 here’s an idea: Replace your page rule with a Worker that does the redirect (it can be the same worker, with an if/else on the hostname). Incoming requests from the outside will hit the worker. But when your Worker makes a subrequest within your same zone, it skips workers to avoid looping.