Skip to content

Instantly share code, notes, and snippets.

@HikariKnight
Last active April 6, 2024 11:04
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save HikariKnight/0c026d33d56cc8c56ab418cf8af0bfb0 to your computer and use it in GitHub Desktop.
Save HikariKnight/0c026d33d56cc8c56ab418cf8af0bfb0 to your computer and use it in GitHub Desktop.
proxmox nginx proxy subdirectory

So I have spent my last 4 days to try get proxmox working with an nginx proxy. Due to how proxmox is built up I had to get clever with some rewrite, break some nginx rules and spend sleepless nights awake to try figure this out (to be honest I am no genius on web servers as I rarely have to touch them). Plus it seems like nobody (from my googling) had managed to set proxmox up to work behind a proxied subdir, understandably so as some of the html generated uses absolute paths for sources instead of relative paths and you need to deal with that.

This is the proxmox.conf file i ended up making (Replace all instances of pve.local:8006 with your internal proxmox ip and port) And replace publicwebsite.com with your website domain

NOTE: THIS IS FOR PVE6 and I have no plans to fix for PVE7 as I do not need it behind a reverse proxy anymore.

upstream php-handler {
    #server 127.0.0.1:9000;
    server unix:/var/run/php/php7.2-fpm.sock;
}

server {
    listen 80;
    listen [::]:80;
    server_name publicwebsite.com;
    # enforce https
    return 301 https://$server_name:443$request_uri;


}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name publicwebsite.com;

    # Change these to match your SSL cert paths
    ssl_certificate /etc/letsencrypt/live/publicwebsite.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/publicwebsite.com/privkey.pem; # managed by Certbot
    ssl_trusted_certificate /etc/letsencrypt/publicwebsite.com/chain.pem; # managed by Certbot
    
    ssl_stapling on; # managed by Certbot
    ssl_stapling_verify on; # managed by Certbot

    # Add headers to serve security related headers
    # Before enabling Strict-Transport-Security headers please read into this
    # topic first.
    #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
    #
    # WARNING: Only add the preload option once you read about
    # the consequences in https://hstspreload.org/. This option
    # will add the domain to a hardcoded list that is shipped
    # in all major browsers and getting removed from this list
    # could take several months.
    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Download-Options "noopen" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    add_header X-Robots-Tag "none" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000" always;

    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;
    
    location ^~ /proxmox {
        # Fix requests so they work through the proxy
        rewrite /proxmox/(.*) /$1 break;
        
        # Proxy to the internal server
        proxy_pass https://pve.local:8006;
        
        # Disable buffering to serve data immediately to clients.
        # Increase timeouts from default 60 seconds to 5 minutes for the console not to close when no data is transferred.
        # Additionally the max_body_size was increased to 5 GB to allow uploads of huge ISOs via the Web UI.
        proxy_buffering off;
        proxy_buffer_size 4k;
        client_max_body_size 5g;
        proxy_connect_timeout 300s;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
        send_timeout 300s;

        # Enable proxy websockets for the noVNC console to work
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Standard proxying headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host publicwebsite.com;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # SSL proxying headers
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Ssl on;
    }

    # Hack to fix the proxmox API when proxied (rewrite example.com/api calls to example.com/proxmox/api calls)
    location ~ ^\/(pve2|api2|novnc|xtermjs|pve-doc|pwt) {
        rewrite ^/(.+) /proxmox/$1;
        proxy_pass https://pve.local:8006$request_uri;
    }

    location / {
        # fix the freaking console for the same reasons
        if ($args ~ ^console) {
            proxy_pass https://pve.local:8006$request_uri;
        }
        
        # If we are not fixing the freaking console, we are a teapot (honestly just remove this if, if you have anything at /)
        if ($args !~ console) {
            # Do other things here if we load publicwebsite.com/
            # I am returning 418 just as a joke
            return 418;
        }
    }
}

And here is a version you can just put somewhere else than /etc/nginx/conf.d (if you do not have multiple domain names) and include it with an include /path/to/proxmox.conf; inside the server block of your main config in /etc/nginx/conf.d/

location ^~ /proxmox {
    # Fix requests so they work through the proxy
    rewrite /proxmox/(.*) /$1 break;
    
    # Proxy to the internal server
    proxy_pass https://pve.local:8006;
    
    # Disable buffering to serve data immediately to clients.
    # Increase timeouts from default 60 seconds to 5 minutes for the console not to close when no data is transferred.
    # Additionally the max_body_size was increased to 5 GB to allow uploads of huge ISOs via the Web UI.
    proxy_buffering off;
    proxy_buffer_size 4k;
    client_max_body_size 5g;
    proxy_connect_timeout 300s;
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;
    send_timeout 300s;

    # Enable proxy websockets for the noVNC console to work
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    # Standard proxying headers
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    # You need to set this manually since we include this file inside a server block on a main config
    proxy_set_header X-Forwarded-Host publicwebsite.com;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # SSL proxying headers
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Ssl on;
}

# Hack to fix the proxmox API when proxied (rewrite example.com/api calls to example.com/proxmox/api calls)
location ~ ^\/(pve2|api2|novnc|xtermjs|pve-doc|pwt) {
    rewrite ^/(.+) /proxmox/$1;
    proxy_pass https://pve.local:8006$request_uri;
}

location / {
    # fix the freaking console for the same reasons
    if ($args ~ ^console) {
        proxy_pass https://pve.local:8006$request_uri;
    }
    
    # If we are not fixing the fucking console, we are a teapot (remove this if you have stuff on /)
    if ($args !~ console) {
        # Do other things here if we load publicwebsite.com/
        # I am returning 418 just as a joke
        return 418;
    }
}

All of this was neccessary because some script on proxmox webui set script, css and request sources to src='/folder/path' instead of src='folder/path'. Never speak or write in absolutes, even in coding...

@yananet
Copy link

yananet commented Mar 19, 2021

The configuration works perfectly in my environment.
I got one 418 from the following request.
https://publicwebsite.com/pwt/css/ext6-pmx.css?ver=2.2-1

location ~ ^\/(pve2|api2|novnc|xtermjs|pve-doc|pwt) {

The above change fixed the issue.

@HikariKnight
Copy link
Author

thanks i noticed a newer proxmox version had changed some stuff but had not had the time to fix it. will update it now

@dbogatyr
Copy link

dbogatyr commented May 4, 2021

Works, thanks!

@hippsabq
Copy link

Thanks for this!! Works great!

@joace
Copy link

joace commented Jan 21, 2022

Thanks for config, I'm assuming it's working on PVE v6, do you have any chance try with v7, I levaraged with necessary changes, e.g. internal IP, domain name, but not working, not sure whether v7 just broke the config.

@HikariKnight
Copy link
Author

v7 might have broken it @joace and i have no longer need for running proxmox behind an nginx proxy anymore.
but all i did was go into a browser and visit the nginx proxy subdir, open the browser dev tools and look at what failed to load in the console and network tabs and then work out what i could from there.

@polarwinkel
Copy link

Thanks a lot for this, HikariKnight!
It is working on my v7 setup as well.
For some reason it brakes though when I change the subpath in any way, like from proxmox to proxmoxx. I have no clue why.

Adding a auth_basic for additional security against crawler works too.

@HikariKnight
Copy link
Author

Thanks a lot for this, HikariKnight! It is working on my v7 setup as well. For some reason it brakes though when I change the subpath in any way, like from proxmox to proxmoxx. I have no clue why.

Adding a auth_basic for additional security against crawler works too.

from what i remember you need to change the location and rewrites to match the new subdir

@polarwinkel
Copy link

from what i remember you need to change the location and rewrites to match the new subdir

I did, but never mind, if I am the only one I guess I forgot it somewhere, but proxmox is fine for me as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment