Home Forums Support Using nginx to serve from non-root path

This topic contains 9 replies, has 4 voices, and was last updated by  bryanp 3 weeks ago.

Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • #24366

    bryanp
    Participant

    The docs explain how to configure nginx to serve invoiceninja from https://www.example.com/.

    Unfortunately I can’t dedicate an entire server for just one app – so I’m trying to serve it from a subdomain: https://apps.example.com/invoiceninja/. (Other paths, e.g. /phpmyadmin/ are for different apps.)

    Serving on a non-root path using nginx is very tricky – if you’ve ever needed to do that before then you know what I mean. Especially if the app doesn’t support a “behind reverse proxy” header like HTTP_X_FORWARDED_HOST. Or one missing/extra trailing slash and nothing works.

    So, does someone have an nginx config to share for this setup? Would be much appreciated, and maybe we could add it to the docs as well… Thanks!

    #24372

    Hillel Coren
    Keymaster
    #24590

    bryanp
    Participant

    Hi thanks for that! Unfortunately that issue is not for a subdirectory. 🙁

    After two days of hacking, I think it’s not possible to do this. (Which is weird because this is a standard use case.)

    It works as foo.example.com, but not as foo.example.com/invoiceninja.

    #24593

    winkelement
    Participant

    I use subdomains for every ‘app’ i run on my single server. A-Record for *.example.com ist pointing to Server-IP so i just write a new entry for every app in my reverse proxy (HAProxy) like whatever.example.com. HAProxy also does the SSL-Termination using a LE Wildcard cert (*.example.com) so SSL is already set up for every possible subdomain. Works fine with invoiceninja.

    #24598

    bryanp
    Participant

    Hey @winkelement, thanks!

    I’m using nginx and certbot, and got InvoiceNinja working on a subdomain root (invoiceninja.example.com).

    Another approach, which is just as common, is to run apps on non-root paths:
    apps.example.com/invoiceninja
    apps.example.com/portainer
    apps.example.com/crm
    apps.example.com/logs
    …etc.

    That’s because most people don’t have wildcard certs, so a cert publicly lists all subdomains, and increases your attack surface. So to conceal your internal apps from the public, you need to use a wildcard cert, or the approach above.

    I noticed you said you use a letsencrypt wildcard cert – I like that option, but most DNS providers don’t offer an API, so it’s impossible to automate dns-01 challenges. And so impossible to use wildcard.

    I’m surprised InvoiceNinja doesn’t support this, as it’s the only app in my server I couldn’t set up this way.

    It’s so good though… oh well! 🙂

    #24599

    bryanp
    Participant

    BTW @winkelement, in your LE wildcard setup, are you using a DNS vendor that offers an API, or are you using a tool like “acme-dns”?

    #24602

    winkelement
    Participant

    I’m using a 5$ Digital Ocean Droplet to handle all routing via haproxy. Once you have at least one Droplet running you can manage DNS settings through DO for every domain you own. DO offers indeed an API so i am using the certbot-dns-digitalocean plugin for the LE wildcard cert(s).

    #24604

    bryanp
    Participant

    @winkelement Thanks for confirming. Unfortunately switching hosters would be a major effort, but it’s good to know it’s possible.

    If anyone stumbles on this thread and knows how to setup IN on a non-root path, please let us know!

    #24620

    GhostZeroQ
    Participant

    I currently have what you’re asking about working for my site. I use NGINX to serve up Invoice Ninja and as reverse proxy. It took me quite a bit of research and watching the logs to figure out what was going wrong.

    Here is my proxy config:

    location ^~ /billing {
      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-Forwarded-Proto $scheme;
      proxy_redirect	off;
      proxy_pass		http://invoice_ninja;
     }

    Here is my nginx config for Invoice Ninja:

    server {
        listen      80;
        server_name _;
    
        root /var/www/invoice_ninja/public/;
        index index.php index.html index.htm;
    
        charset utf-8;
    
        location / {
            try_files $uri $uri/ /index.php?is_args$args;
        }
    
        location = /favicon.ico { access_log off; log_not_found off; }
        location = /robots.txt  { access_log off; log_not_found off; }
    
        access_log  /var/log/nginx/invoice-ninja.access.log;
        error_log   /var/log/nginx/invoice-ninja.error.log;
    
        location /billing {
            alias /var/www/invoice_ninja/public;
            try_files $uri $uri/ @billing;
    
            location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/run/php/php7.3-fpm.sock;
            fastcgi_index index.php;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            fastcgi_intercept_errors off;
            fastcgi_buffer_size 16k;
            fastcgi_buffers 4 16k;
            }
        }
    
        location @billing {
            rewrite /billing/(.*)$ /billing/index.php?/ last;
        }
    
        location ~ /\.ht {
            deny all;
        }
    }

    Invoice Ninja is accessible from the /billing subfolder on my main site. The big piece to getting this working were the try_files $uri $uri/ @billing; and location @billing { rewrite /billing/(.*)$ /billing/index.php?/ last; }portions.
    Getting the rewrite working properly was the largest hurdle.

    Hope it helps.

    #24624

    bryanp
    Participant

    @ghostzeroq Thanks for posting your solution!

    I tried your config… unfortunately it didn’t work for me. I actually tried that approach before. The only difference from your setup is I’m running in docker via a named network endpoint (not a socket). Or maybe it’s something else.

    The error I get is from the container’s php-fpm process: Primary script unknown.

    Also I’m unsure how you got it working without stripping the /billing/ prefix in the split_path directive… Does your install path have a /billing/ component in it maybe? e.g. /var/www/billing/app/public/ or something like that?

Viewing 10 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic.

Posted in: