When setting up Nginx as a caching proxy for npm registry behind corporate firewalls, we often need to chain proxies. The primary obstacle is making Nginx (which ignores http_proxy
environment variables) properly route requests through an intermediate proxy while maintaining caching functionality.
The ideal flow should be:
Client → Nginx (caching) → Corporate Proxy → registry.npmjs.org
Standard Nginx proxy configurations fail because:
- Nginx doesn't honor OS-level proxy settings
- Direct proxy_pass to registry.npmjs.org gets blocked
- Simple proxy chaining breaks npm's URL structure
Here's the working configuration that maintains caching while routing through your corporate proxy:
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 / {
# Preserve original request URI
set $target_uri $uri;
# Route through corporate proxy
proxy_pass http://proxy.abc.lan:1234;
# Critical headers for proxy chaining
proxy_set_header Host registry.npmjs.org;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Cache configuration
proxy_cache npm;
proxy_cache_valid 200 302 365d;
proxy_cache_valid 404 1m;
# URL rewriting
proxy_redirect ~^http://registry.npmjs.org(.*)$ http://classen.abc.lan$1;
sub_filter 'registry.npmjs.org' 'classen.abc.lan';
sub_filter_once off;
sub_filter_types application/json;
}
}
1. Proxy Headers
The proxy_set_header Host
directive tells your corporate proxy where to route the request while maintaining the original npm registry's hostname.
2. Cache Preservation
All caching directives remain functional because Nginx handles caching before the request reaches the corporate proxy.
3. URL Rewriting
The combination of proxy_redirect
and sub_filter
ensures links in npm responses point to your local cache.
Problem: Getting 302 redirects instead of cached content
Solution: Ensure your proxy_redirect
pattern matches npm's response URLs exactly
Problem: Corporate proxy rejects requests
Solution: Add authentication headers if required:
proxy_set_header Proxy-Authorization "Basic [base64-encoded-creds]";
For environments requiring SSL inspection:
location / {
proxy_ssl_server_name on;
proxy_ssl_verify off; # Only if necessary
proxy_pass https://proxy.abc.lan:1234;
# ... rest of config remains same
}
For handling WebSocket connections (needed for some npm operations):
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
I recently faced a challenge where I needed to set up an npm mirror cache using Nginx on a server behind a corporate firewall. While my initial configuration worked perfectly on an unrestricted network, the firewall-blocked environment required some creative proxying.
Here's the basic Nginx setup that works without firewall restrictions:
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;
}
}
The server couldn't directly access registry.npmjs.org, but we had an internal proxy (proxy.abc.lan:1234) that could bypass the firewall. While curl worked with the proxy:
http_proxy=http://proxy.abc.lan:1234/ curl http://registry.npmjs.org
Nginx doesn't respect the http_proxy environment variable, requiring a different solution.
After some research and testing, I found that we need to use Nginx's rewrite directive combined with proxy_pass:
server {
listen 80;
server_name classen.abc.lan;
location / {
resolver 8.8.8.8; # DNS resolver required
rewrite ^(.*)$ "http://registry.npmjs.org$1" break;
proxy_pass http://proxy.abc.lan:1234;
# Original caching configuration
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;
}
}
The critical components are:
rewrite
directive to modify the request URL before passing to the proxybreak
flag to prevent further rewrite processingresolver
to enable DNS resolution through the proxy- Maintaining all original caching functionality
After implementing this solution, you can verify it works by:
curl -v http://classen.abc.lan/your-package
You should see:
- The request hits your Nginx server
- Nginx forwards it through your internal proxy
- The response gets cached according to your rules
- All registry.npmjs.org references are rewritten to your domain
When implementing this proxy chain:
- Monitor cache hit ratios to ensure proper caching
- Adjust proxy_read_timeout if dealing with slow proxy connections
- Consider adding proxy_headers to ensure proper request forwarding