3

I have an HTML5 app written in static html/js/css (it's actually written in Dart, but compiles down to javascript). I'm serving the application files via CDN, with the REST api hosted on a separate domain. The app uses client-side routing, so as the user goes about using the app, the url might change to be something like http://www.myapp.com/categories. The problem is, if the user refreshes the page, it results in a 404.

Are there any CDN's that would allow me to create a rule that, if the user requests a page that is a valid client-side route, it would just return the (in my case) client.html page?

More detailed explanation/example

The static files for my web app are stored on S3 and served via Amazon's CloudFront CDN. There is a single HTML file that bootstraps the application, client.html. This is the default file served when visiting the domain root, so if you go to www.mysite.com the browser is actually served www.mysite.com/client.html.

The web app uses client-side routing. Once the app loads and the user starts navigating, the URL is updated. These urls don't actually exist on the CDN. For example, if the user wanted to browse widgets, she would click a button, client-side routing would display the "widgets" view, and the browser's url would update to www.mysite.com/widgets/browse. On the CDN, /widgets/browse doesn't actually exist, so if the user hits the refresh button on the browser, they get a 404.

My question is whether or not any CDNs support looking at the request URI and rewriting it. So, I could see a request for /widgets/browse and rewrite it to /client.html. That way, the application would be served instead of returning a 404.

I realize there are other solutions to this problem, namely placing a server in front of the CDN, but it's less ideal.

w.brian
  • 16,296
  • 14
  • 69
  • 118
  • Which URL tries the browser to fetch from the CDN and at which URL can the file be found? For me this is not clear from your question, can you please provide a concrete example. – Günter Zöchbauer Jul 22 '14 at 16:07
  • I updated my question. Let me know if it's still unclear! – w.brian Jul 22 '14 at 17:52
  • I still have troubles to understand why the current browser location is forwarded to your CDN. Does your CDN respond to the same domain as your site? Do you have relative URLs for your resources hosted on the CDN instead of absolute ones? How/why does your CDN recognize `/client.html`? – Günter Zöchbauer Jul 22 '14 at 18:02
  • What is deployed on the CDN is the output from `pub build`. So all the static assets needed to serve up the web app. `client.html` is what bootstraps the app, and actually exists on the CDN root. Therefore, `www.myapp.com/client.html` actually exists (I've configured `client.html` to be the "default" file served when visiting the website root, so the user can just go to `www.mysite.com` and they're served `client.html`. The current browser location is forwarded to the CDN because client-side routing updates the browser's url. When the user hits the refresh button, that url is requested. – w.brian Jul 22 '14 at 18:09
  • I see, I somehow missed that you serve the entire client code from the CDN I assumed just some resources like *.css, *.img, .... I know client side routing only from Angular.dart (and the last time is already a few months in the past) but as far as I remember routing only changed the parts after the `?` in `client.html?`. How do you do the routing? – Günter Zöchbauer Jul 22 '14 at 18:15
  • I am using angular.dart routing, which doesn't use query params for routing. `/browse` and `/browse/widgets` are typical html5 client-side routes -- no query params or hashbangs necessary. The problem I'm running into is an obvious shortcoming, however. – w.brian Jul 22 '14 at 18:23
  • I think I understand now, but I don't think I can provide a solution. – Günter Zöchbauer Jul 23 '14 at 05:19
  • @w.brian did you find any solution? I have the same problem and I solved it (partially) generating a lot of folders in Amazon S3 with the index.html (in your case client.html) inside them. It works only for main pages and if you have a few pages. But if you have tons of pages it doesn't work. Thanks! – Fernando P. G. Sep 15 '16 at 11:50

2 Answers2

3

I do this using CloudFront, but I use my own server running Apache to accomplish this. I realize you're using a server with Amazon, but since you didn't specify that you're restricted to that, I figured I'd answer with how to accomplish what you're looking to do anyway.

It's pretty simple. Any time you query something that isn't already in the cache on CloudFront, or exists in the Cache but is expired, CloudFront goes back to your web server asking it to serve up the content. At this point, you have total control over the request. I use the mod_rewrite in Apache to capture the request, then determine what content I'm going to serve depending on the request. In fact, there isn't a single file (spare one php script) on my server, yet cloudfront believes there are thousands. Pretty sure url rewriting is standard on most web servers, I can only confirm on lighttp and apache from my own experience though.

More Info

All you're doing here is just telling your server to rewrite incoming requests in order to satisfy them. This would not be considered a proxy or anything of the sort.

The flow of content between your app and your server, with cloudfront in between is like this:

appRequest->cloudFront
         if cloudFront has file, return data to user without asking your server
         for the file.

         If cloudFront DOESN'T have the file (or it has expired), go back to 
         the origin server and ask it for a new copy to cache.

So basically, what is happening in your situation is this:

A)app->ask cloudfront for url cloud front doesn't have
B)cloudfront then asks your source server for the file
C)file doesn't exist there, so the server tells cloudFront to go fly a kite
D)cloudFront comes back empty handed and makes your app 404
E)app crashes and burns, users run away and use something else.

So, all you're doing with mod_rewrite is telling your server how it can re-interpret certain formatted requests and act accordingly. You could point all .jpg requests to point to singleImage.jpg, then have your app ask for:

www.mydomain.com/image3.jpg
www.mydomain.com/naughtystuff.jpg

Neither of those images even have to exist on your server. Apache would just honor the request by sending back singleImage.jpg. But as far as cloudfront or your app is concerned, those are two different files residing at two different unique places on the server.

Hope this clears it up.

http://httpd.apache.org/docs/current/mod/mod_rewrite.html

  • 1
    I appreciate the answer... would this be considered a reverse proxy? – w.brian Sep 26 '14 at 21:55
  • Updating answer with more explanation. –  Sep 26 '14 at 22:16
  • @w.brian I added more explanation of what's happening. I wanted to throw in some actual apache config code as examples but it's after school/dinner time and so it's a little crazy right now. lol if you need more clarification let me know I'll try and update later. –  Sep 26 '14 at 22:29
  • short answer is no. Think of it like creating shortcuts to REAL files (or generated ones) on the fly to satisfy requests, or filter, or whatever your needs are. –  Sep 26 '14 at 22:30
1

I think you are using the URL structure in a wrong way. the path which is defined by forward slashes is supposed to bring you to a specific resource, in your example client.html. However, for routing beyond that point (within that resource) you should make use of the # - as is done in many javascript frameworks. This should tell your router what the state of the resource (your html page or app) is. if there are other resources referenced, e.g. images, then you should provide different paths for them which would go through the CDN.