My question is about using Nginx as a proxy behind another proxy. (Somewhat confusing.)

I want to set up Nginx so it acts as a caching proxy server to an npm mirror. Here is the link: http://eng.yammer.com/a-private-npm-cache/

On my local machine, which is not restricted by a firewall, the following configuration works fine:

proxy_cache_path /var/cache/npm/data levels=1:2 keys_zone=npm:20m max_size=1000m
inactive=365d;
proxy_temp_path /var/cache/npm/tmp;

server {
   listen 80;
   server_name classen.abc.lan;
   location / {
      proxy_pass http://registry.npmjs.org/;
      proxy_cache npm;
      proxy_cache_valid 200 302 365d;
      proxy_cache_valid 404 1m;
      sub_filter 'registry.npmjs.org' 'classen.abc.lan';
      sub_filter_once off;
      sub_filter_types application/json;
   }
}

Now I want to apply it to a server that is behind an additional firewall. In the logs, I can confirm that it accesses the correct upstream IP, but the request fails because of the internal firewall.

We have one internal proxy, which I can use to bypass the firewall, for example:

$ curl http://registry.npmjs.org
curl: (7) couldn't connect to host
$ http_proxy=http://proxy.abc.lan:1234/ curl http://registry.npmjs.org
... succeeds ...

This trick does not work with Nginx, as it ignores the http_proxy environment variable. After reading the documentation, I still could not figure out how to modify the configuration, so that it can use the proxy internally.

Is it possible to combine both solutions? It is important that the caching still works, otherwise, you can just use the external mirror registry.npmjs.org directly.

Maybe, Nginx should use the internal proxy (proxy.abc.lan) as proxy_pass, but then how does the internal proxy know that the request should be sent to the external npm mirror (http://registry.npmjs.org)?

Update to Lukas answer

I tried Lukas solution:

rewrite ^(.*)$ "http://registry.npmjs.org$1" break;
proxy_pass http://proxy.abc.lan:1234;

The logs show that the URL is rewritten but it results in a redirect (triggered by curl classen.abc.lan/test-url):

2014/03/24 11:31:16 [notice] 13827#0: *2 rewritten redirect: "http://registry.npmjs.org/test-url", client: 172.18.40.33, server: classen.abc.lan, request: "GET /test-url HTTP/1.1", host: "classen.abc.lan"

The result of the curl call is not the expected JSON string from http://registry.npmjs.org but a html page generated by Nginx:

$ curl classen.abc.lan/test-url
<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>nginx/1.4.7</center>
</body>
</html>

The issue with Lukas's solution is HttpRewriteModule , which automatically turns everything with http(s) at the front into a 302.

If you instead do the rewrite in two stages - the second one 'break' - it should work. e.g.

rewrite ^(.*)$ "://registry.npmjs.org$1";
rewrite ^(.*)$ "http$1" break;
proxy_pass http://proxy.abc.lan:1234;

I suspect there's a nicer way to do this, but it appears to work.

  • This really worked!!! I am using nginx as forward proxy behind a corporate proxy. My config: rewrite ^(.*)$ "://$http_host$uri$is_args$args" break; rewrite ^(.*)$ "http$uri$is_args$args" break; proxy_pass http://proxy.abc.lan:3128; – Prashant Borde Oct 1 '16 at 18:30
  • 1
    Should the first rewrite not "break"? Also, I think people would appreciate it if you can mention https proxy is not supported by Nginx. – blurrcat Oct 11 '17 at 5:40
  • @blurrcat you are right. In addition, it does not support reverse proxy to https website. – ipcjs Sep 28 at 8:31

RFC 2616, Section 5.1.2 states

The absoluteURI form is REQUIRED when the request is being made to a proxy.
[...]
An example Request-Line would be:

  GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1

So what you are supposed to do is pass the request to the proxy with those modified directives:

rewrite ^(.*)$ "http://registry.npmjs.org$1" break;
proxy_pass http://proxy.abc.lan:1234;

According to the nginx docs, using rewrite ... break; will force nginx to use the rewritten URI (now an absolute URI as the protocol requires) instead of trying to build it from the proxy_pass directive.

  • Thanks for your answer. Unfortunately, I could not get it to work. Maybe I am missing something. I edited my answer to show what I have tried. – Philipp Claßen Mar 24 '14 at 10:43

I think it may be simpler than either of the examples above. They are using rewrite to rewrite the url, I think you can use proxy_pass but pass the url to the proxy setting the host header param to the location you want to go to. e.g.

http {

  upstream corporate_proxy  {
      server web-proxy.mycorp.com:8080;
  }

server {
   listen 80;
   server_name classen.abc.lan;
   location / {
      proxy_pass_header on;
      proxy_set_header Host "registry.npmjs.org";
      proxy_pass http://corporate_proxy;
      proxy_cache npm;
      proxy_cache_valid 200 302 365d;
      proxy_cache_valid 404 1m;
      sub_filter 'registry.npmjs.org' 'classen.abc.lan';
      sub_filter_once off;
      sub_filter_types application/json;
   }
}

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.