Nginx Reverse Proxying to Node.js with Rewrite

10,577

I have made nginx serve static files without even passing those requests to node by adding location directive to the app's nginx configuration file (which is included in nginx.conf):

location ~ /(img|js)/ {
    rewrite ^(.*)$ /public/$1 break;
}

location / {
    proxy_pass http://localhost:3000/;
    ...
}

In case request comes to /img or /js directory nginx serves files from /public/img or /public/js directory respectively. All other requests are proxied to node.

You can add more directories if you need (like /css or /views, if you store templates there that you want to use both in node and in browser) and have any directory structure inside those directories, nginx just prepends /public to them and gets files from there without your node app even knowing about it.

Share:
10,577

Related videos on Youtube

Maros
Author by

Maros

Updated on June 20, 2022

Comments

  • Maros
    Maros almost 2 years

    I have several apps running behind an Nginx reverse proxy, one of which is a Node server with Express.js. I'm proxying domain.com/demo/app/<path> to localhost:7003/<path> using this Nginx config:

    http {
    
        ...
    
        server {
    
            listen 80;
            server_name domain.com;
    
            ...
    
            location /demo/app {
    
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Scheme $scheme;
    
                rewrite ^/demo/app/?(.*) /$1 break;
                proxy_pass http://localhost:7003;
            }
    
            ...
        }
    }
    

    This works great and app receives requests as if it was rooted at /. The problem is app handles its own static files and might make requests for routes such as css/app.css or images/image.jpg. But because of the reverse proxy, these actually exist at /demo/app/css/app.css and /demo/app/images/image.jpg respectively.

    I've solved this by getting Nginx to pass to Node a custom header indicating the root path, which the Node server prepends to the URLs of all subsequent requests. But now my code is littered with these root path strings. For example, part of my back-end templates:

    link(rel='stylesheet', href="#{basePath}/css/base.css")
    link(rel='stylesheet', href="#{basePath}/css/skeleton.css")
    link(rel='stylesheet', href="#{basePath}/css/layout.css")
    

    What's a more elegant way to handle this? Isn't there a way to get Nginx to recognize requests coming from an upstream server and automatically forward them to that server?

  • Maros
    Maros over 11 years
    How do you handle AJAX requests? I'd like to not have to prepend AJAX URLs in the JavaScript with /demo/app
  • esp
    esp over 11 years
    With the configuration above all requests (other than to /img/... and /js/...) will be proxied to node.js assuming it listens to the port 3000. location / matches all requests that were not matched by the previous location directive.
  • esp
    esp over 11 years
    If you need to make your app process requests as if they come to / you still can rewrite requests the way you did... I would prefer to have this routing inside an application but it is my personal preference... I am not sure I understood correctly what you want to achieve, but the only way not to include {basepath} in links is to rewrite requests the way I did. It also allows not to process static files in node, but if you want to, you still can proxy them too...