URL Masking Question


#1

Hi Cloudflare Community!

I could use some help. I’m working at a software company, lets say sweetapp123.com

We have the ability for customers to do DNS CNAME to brand a subdomain as our app. So they might points sweetapp.companyurl.(com) to company.sweetapp123.(com) and everything works great.

But recently one of our customers has used a CDN to do something different… rather than setting up subdomain.companyname.(com) they did companyname.(com)/sweetapp (they used a directory).

I would love to figure out how to do this but I’m at a lost… and I dont even really know what to call it… so I am referring to it as URL Masking.

Is this the sort of thing a CDN can do by proxying a web page and delivering the content on a domain it manages? Can Cloudflare do this?

Thank you!!

Mario


#2

It can be done by proxying the content as you would two different IPs based on the path instead of the subdomain.

Cloudflare can do this by using Workers. There you can configure a specific path to do what you want.

Workers are a paid products (5$/month for the first 10M queries, 50 cents/month for each additional million) which requires you to write the code yourself (JavaScript o WebAssembly).


#3

Thanks Matteo, that was really helpful.

I’m a little confused about the custom code required (JS or WebAssembly)

I’m new to the concept of Workers within Cloudflare. I was able to discuss with someone using AWS Cloudfront that they achieve this using “a new origin and pattern matching behavior to route requests for the page to your application”

Matteo, do you think I can do this in Cloudflare using something native to cloudflare or do I really need a severless custom application?

Mario


#4

On Cloudflare you need to write code, that is for sure. It’s not that complicated and if you need some basic code ask and as soon as I can will give you a basic sample. It shouldn’t be more than a few lines for a single rewrite.


#5

Yea, Matteo that would be a huge help if you can toss some basic code up here for me…

Basically I have this need.

Client wants to have www.website.com/directory show everything on my app which is customer.myapp.con/directory - its essentially using their root domain and a subdirectory as a proxy for a page we host.


#6

Alright, so having the identical directory implies things even further. Gonna use your suggested domains.

addEventListener("fetch", event => {
  let requestURI = new URL(event.request.url);

  if (requestURI.hostname == "www.website.com" && requestURI.path.startsWith("/directory")) {
    event.respondWith(rewriteURL(event.request));
  }
})

async function rewriteURL(request) {
  var URI = new URL(request.url);

  URI.hostname = "customer.myapp.com";

  return fetch(URI, request);
}

Some comments on the code:

  1. the first part of JS code has more than strictly necessary, but I always prefer to decide the scope of each reply. If you have a single function in your code and a single route you could simply use the line event.respondWith(rewriteURL(event.request));. This is more future proof in case you decide to add more to the Worker script.
  2. This code currently works with a single customer, but you can rewrite it slightly to allow more than one domain. Shouldn’t be necessary since it runs on the customer’s website which has only one domain.
  3. The fetch(URI, request) request passes all data to the other URI (customer.myapp.com), including the previous host header. You need to make sure that your server replies to the requests on a different host than its own, but it’s not a problem limited to this solution.

Try and check if everything works, there could be particular cases and I never actually implemented it myself.


#7

Hey Matteo,

I really appreciate you drawing this up for me… I couldn’t get it to work yet though but I’m hopeful. For the sake of sharing some real URL’s with you I’ve set up an extra domain I have on cloudflare and purchased a worker for it. I’ll give you the breakdown of where I’m at:

My domain is www.bdiq.com/directory and I am attempting to proxy/render https://agencies.partnerpage.io/directory/ which is my product page. Below I have modified that code you shared with me… see the example here:

addEventListener(“fetch”, event => {

let requestURI = new URL(event.request.url);

if (requestURI.hostname == “www.bdiq.com” && requestURI.path.startsWith("/directory")) {

event.respondWith(rewriteURL(event.request));

}

})

async function rewriteURL(request) {

var URI = new URL(request.url);

URI.hostname = “agencies.partnerpage.io”;

return fetch(URI, request);

}

I created a route of “www.bdiq.com/directory” andmy hope is that I can have www.bdiq.com/directory and it will display agencies.partnerpage.io/directory but remain on my bdiq.com URL - the editor was telling me there was some issue with the JS: Uncaught (in response) TypeError: Cannot read property ‘startsWith’ of undefined

I remembered your comment about how I can remove part of the code and it was more to make the code robust and after reviewing your comment I changed the code to be

addEventListener(“fetch”, event => {

let requestURI = new URL(event.request.url);

event.respondWith(rewriteURL(event.request));

})

async function rewriteURL(request) {

var URI = new URL(request.url);

URI.hostname = “agencies.partnerpage.io”;

return fetch(URI, request);

}

At this point the URL is redirecting to the right place, but its not maintaining the bdiq.com domain… perhaps I am missing something more.

Let me know if you have any additional idea for me on this :slight_smile:

Thank you!

Mario T


#8

Hey Mario,

the issue here seems that the http://www.bdiq.com/directory URL is redirecting to the other page. Do you have a Page Rule set to do so?

It is currently returning a 301, I assume it’s not even going through the Worker:

curl -I http://www.bdiq.com/directory
HTTP/1.1 301 Moved Permanently
Date: Wed, 16 Jan 2019 10:43:38 GMT
Content-Type: text/html
Connection: keep-alive
Set-Cookie: __cfduid=d45e099a5e52e059670998f25875c6b221547635418; expires=Thu, 16-Jan-20 10:43:38 GMT; path=/; domain=.bdiq.com; HttpOnly
Location: https://agencies.partnerpage.io/directory
CF-RAY: 499ffb7647546f66-FCO
Server: cloudflare

Another thing, but I am not sure how you set it up, the route must be www.bdiq.com/directory/*, with the asterisk to match all subpaths.


Finally what I meant was the opposite actually, I put the final sentence in the wrong spot. The code I wrote is more future-proof. I should have said:

  1. the first part of JS code has more than strictly necessary, but I always prefer to decide the scope of each reply. This is more future proof in case you decide to add more to the Worker script. If you have a single function in your code and a single route you could simply use the line event.respondWith(rewriteURL(event.request));.

P.S.: for the code you can show it a better and easier to read way by doing a block with these three before and after: ```. They should be available in the keyboard or are available in the toolbar at the top, albeit only for inline code which requires you to copy and triplicate them.


#9

Yesterday I had set up a Page Rule, but I removed it yesterday also.

Right now I can confirm there is no pageful configured there in Cloudflare. I just double checked and I have all 3 page rules are still open to me for BDIQ.com

Mario


#10

That’s strange. Try adding this line before the current return. This should not request anything outside and simply reply.

return new Response('Hello');

#11

I’ve done it.

I ran the “test” in the editor and I received:

content-length: 5

content-type: text/plain;charset=UTF-8

Hello


#12

Another part of the issue here could be that partnerpage.io supports customer directories.

I think the 301 you see is from agencies.partnerpage.io -> agencies.partnerpage.io/directory


#13

Now try doing:

return new Response(URI);

#14

content-length: 32
content-type: text/plain;charset=UTF-8

https://agencies.partnerpage.io/


#15

I don’t see that from my end. Did you publish the script?


#16

Not sure! Just clicked deploy now.


#17

Not currently with a computer in front, but could it be that the remote host forces HTTPS? That whould explain the redirect we see to the current page. Try accessing over HTTPS the first link with the original code.

Theoretically you could force the HTTPS rewrite in the URL inside the worker, but it would be easier and better to force the “customer’s” website to HTTPS.


#18

I was able to see the URL posted on the page using the previous code.

I reverted back to the code from the beginning and there was a syntax error so I made a small change to the code at the beginning as you suggested in your comments.

I attempted HTTPS but still no luck… its def possible that we do a redirect to https on the PartnerPage.io server but at the same time I changed the URL to include HTTPS and that should direct directly there now:

Current Code below:
addEventListener(“fetch”, event => {
let requestURI = new URL(event.request.url);
event.respondWith(rewriteURL(event.request));
})

async function rewriteURL(request) {
var URI = new URL(request.url);

URI.hostname = “https://agencies.partnerpage.io”;

return fetch(URI, request);
}


#19

You can’t do it that way, you would have a double scheme as the hostname shouldn’t include it. It’s under scheme.

I’ll do a couple of tests myself since it’s difficult to debug remotely. Will get back to you as soon as possible.


#20

So, I tried on my domain and everything works fine with a simple copy and paste of the original code, barring the actual small error which could have cause the issue (pathname, not path, but it gave me a rendering error though, so no idea where you got the redirect). Including the protocol (not scheme, sorry…) it becomes this.

addEventListener("fetch", event => {
  let requestURI = new URL(event.request.url);

  if (requestURI.hostname == "www.bdiq.com" && requestURI.pathname.startsWith("/directory")) {
    event.respondWith(rewriteURL(event.request));
  }
})

async function rewriteURL(request) {
  var URI = new URL(request.url);

  URI.protocol = "https";
  URI.hostname = "agencies.partnerpage.io";

  return fetch(URI, request);
}

You need to fix CORS though, I can’t access the resources on your domain through mine. It loads the basic HTML, but the data doesn’t load.