-2

I have a nginx server running at www.example.com.

Within it, I have set up a reverse proxy to a static website of mine hosted with Zeit now.

location /apps/app1/ {
    proxy_pass https://app1.username.now.sh;
}

However, when I visit www.example.com/apps/app1, I get the correct browser tab name ('React App' in my case as my static website is made with React and the default name is 'React App').

In the browser console, I see the following errors:

GET https://www.example.com/static/css/1.621e483e.chunk.css net::ERR_ABORTED 404 (Not Found)
GET https://www.example.com/manifest.json 404 (Not Found)
Manifest: Line: 1, column: 1, Unexpected token.

I had a similar issue when my static website was hosted at Netlify. The Netlify customer service told me that they don't support reverse proxying, but that I could use their own redirects.

Why am I not able to reverse proxy to a static website at an external URL? Is there a way to fix this?

Update 1: I'm currently trying to reverse proxy bluprince13.com/apps/renting-vs-buying/ to renting-vs-buying.bluprince13.now.sh

bluprince13
  • 4,607
  • 12
  • 44
  • 91

4 Answers4

1

Probably your path is not matching the statics, since when it loads it's looking for statics in / context and not in /apps/app1/. So, it will not match your location and not proxy to your site.

I would recommend you to use the same path in your proxy and in your host, or you will need to rewrite the URLs, but you will need to find every path pattern for the statics your application serves.

If it's a possibility, try to use sub-domains in the / context and put same basic headers to help your application to resolve better.

server {
    listen 80;
    server_name app1.example.com;

    location / {
        proxy_set_header HOST $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_pass https://app1.username.now.sh/;
    }
}

Hope it helps.

Just a note: I think you should avoid using reverse proxy. Are you sure there isn't another better solution like setting a CNAME record DNS?

Fabiano
  • 1,344
  • 9
  • 24
1

Here's what worked for me, but I only figured it out with the help of the other guys who answered below. In hindsight, it seems so simple and I feel silly for having not figured it out myself!

The response's HTML contains absolute URLs for CSS and JS files. The browser initially loads the HTML page from the /apps/app1 prefix, but then it tries to load assets from /static, /favicon, /css etc. But, there's nothing there!

The problem is that the app uses absolute referencing. All I needed to do was change my app to use relative referencing instead. With React, you can do this easily by putting this in your package.json (see docs here):

"homepage": ".",

Now, the browser will correctly load assets from /apps/app1/static, /apps/app1/favicon, /apps/app1/css etc.

bluprince13
  • 4,607
  • 12
  • 44
  • 91
0

Should you do it?

I would not recommend doing this, for one simple reason: since you don't control the upstream, and they explicitly told you that they don't support your scenario, they could decide to block your proxy server at any time, and then your whole website would be down due to the unsupported setup. This is what happened with lots of folks who tried fixing the broken Tumblr with Cloudflare back in the day (back in the day, it was very common to be getting blank pages from Tumblr, so, putting Cloudflare in front of it, actually did speed it up, and fixed the blank pages issue).


How to do it.

However, if you still want to do it with nginx, it's really simple, and this is a very popular question around nginx:

You basically just have to map the /apps/app1/ on the front-end to / on the backend, and it can be done like this — notice that the trailing slash is very important here — as per http://nginx.org/r/proxy_pass (which refers to it as the presence of "URI"):

location /apps/app1/ {
    proxy_pass https://app1.username.now.sh/; # DO NOT REMOVE trailing slash!
}
cnst
  • 25,870
  • 6
  • 90
  • 122
  • I added the trailing slash. Still doesn't work though. – bluprince13 May 24 '19 at 20:35
  • 1
    @bluprince13 the problem is that your apps use absolute instead of relative URLs; it won't work properly; one option might be to use $http_referer to determine which app requests which resource. – cnst May 24 '19 at 23:48
0

It's possible, but has its technical downsides (besides an evident consideration that you might be violating an upstream's Terms of Service).

My whole configuration looks like the following:

server {
  server_name localhost;
  listen *:8000;

  location /apps/ {
    sub_filter 'href="/c' 'href="/apps/c';
    sub_filter 'href="/f' 'href="/apps/f';
    sub_filter 'href="/m' 'href="/apps/m';
    sub_filter 'href="/s' 'href="/apps/s';
    sub_filter 'src="/s'  'src="/apps/s';
    sub_filter_once off;
    proxy_set_header Accept-Encoding "";
    proxy_pass https://renting-vs-buying.bluprince13.now.sh/;
  }
}

Here Nginx is configured to replace some parts of a response using its sub_filter module. We specify what strings to look for and replace with. Since your HTML is likely produced by something like Webpack and has no newlines, we need to repeat replacements in every line, that is why sub_filter_once off.

Regarding Accept-Encoding. Nginx is able to perform replacements in text/html responses. Only. Which means no compression. And since the backend likes to respond with compressed content, we ask it not to do so.

Few notes:

  • the static list of filters is fragile: your application may break when some new prefix is added on backend side;
  • you are wasting bandwidth between proxy and the backend, because using compression is not possible.

I would suggest that the best option would be to change your application so that it responds under /apps prefix. And you would immediately avoid all the headache.

Alexander Azarov
  • 12,971
  • 2
  • 50
  • 54
  • Thanks for your answer. I didn't understand what the c f m s subfilters are for though. – bluprince13 May 24 '19 at 20:40
  • 1
    Every answer here tells you the same: that the response's HTML contains absolute URLs for CSS and JS files. When a browser sees that absolute URL, it fetches it, well, from this absolute path, despite your page is located at `/apps`. So your browser initally loads the page from `/apps` prefix and then it tries to load assets from `/static`, `/favicon`, `/css` (do you note the first letters of filter strings?) – Alexander Azarov May 25 '19 at 06:15
  • Are you using Chrome DevTools console? Open a new tab in Chrome, open Dev tools, go to Network tab and then open your application's URL. And you shall be able to see what requests browser makes, their complete URLs and which of them fail or not. – Alexander Azarov May 25 '19 at 06:25