Reverse proxy configuration

Thanks for the example. This is just want I was looking for to get started with an niginx reverse proxy for OctoPrint!

I'm using Nginx in a docker container with built in letsencrypt support and SSL, but the principle should be the same. Here's what I've got to serve up Octoprint on a subfolder of my main domain and with a webcam (Logitech C920) working fine.

My Raspberry Pi has a static IP on 192.168.0.240.
I can reach Octoprint on https://server.com/octoprint & the webcam on https://server.com/octocam

This reverse proxy configuration works "out the box" with no edits required to /boot/octoprint.txt

    location /octoprint/ {
        proxy_pass http://192.168.0.240/;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;
        proxy_set_header X-Script-Name /octoprint;
        proxy_http_version 1.1;
        client_max_body_size 0;    
    }

    location /octocam/ {
        proxy_pass http://192.168.0.240:8080/;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;
        proxy_set_header X-Script-Name /octocam;
        proxy_http_version 1.1;
        client_max_body_size 0;    
    }

Once you've confirmed that the above two are working you can configure Octoprint in the webui with a webcam stream URL of https://server.com/octocam/?action=stream and a snapshot URL of https://server.com/octocam/?action=snapshot

Obviously the IP of 192.168.0.240 and server.com will be replaced by whatever you are using in your setup.

Just posting here in case anybody is struggling.

5 Likes

Any chance you can share your subdomain config info? I'm using letsencrypt in a docker image on an R710 server and trying to figure out the proper subdomain config file

Thanks for posting that!

I Copy pasted that into a file "octoprint.subfolder.conf" in the config files for my LinuxServer/LetsEncrypt container, and it worked perfectly.

I was previously working fine using subdomains, but I needed to change my certificate so I restructured LetsEncrypt.

Here was the working config I had for a LetsEncrypt Subdomain:
@jagr200

# make sure that your dns has a cname set for deluge and that your deluge container is not using a base url

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name octoprint.*;

    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    # enable for ldap auth, fill in ldap details in ldap.conf
    #include /config/nginx/ldap.conf;

    location / {
        # enable the next two lines for http auth
        #auth_basic "Restricted";
        #auth_basic_user_file /config/nginx/.htpasswd;

        # enable the next two lines for ldap auth
        #auth_request /auth;
        #error_page 401 =200 /login;

        # DO NOT INCLUDE THE DEFAULT - OVERRIDES BELOW
		#include /config/nginx/proxy.conf;
		
		#Settings per this link: https://community.octoprint.org/t/reverse-proxy-configuration-examples/1107
		proxy_set_header Host $http_host;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		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;
		proxy_http_version 1.1;
		client_max_body_size 0;    
		
		#Settings from the proxy.conf that need to be imported since import was disabled
			client_body_buffer_size 128k;
			#Timeout if the real server is dead
			proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;

			# Advanced Proxy Config
			send_timeout 5m;
			proxy_read_timeout 240;
			proxy_send_timeout 240;
			proxy_connect_timeout 240;

			# TLS 1.3 early data
			proxy_set_header Early-Data $ssl_early_data;

			# Basic Proxy Config
			proxy_set_header X-Forwarded-Proto https;
			proxy_set_header X-Forwarded-Host $host;
			proxy_set_header X-Forwarded-Ssl on;
			proxy_redirect  http://  $scheme://;
			proxy_cache_bypass $cookie_session;
			proxy_no_cache $cookie_session;
			proxy_buffers 32 4k;
			proxy_headers_hash_bucket_size 128;
			proxy_headers_hash_max_size 1024;

		
		#Required in this file
        resolver 127.0.0.11 valid=30s;
        set $upstream_octoprint 192.168.1.105;
        proxy_pass http://$upstream_octoprint;
    }
}
2 Likes

I have a different issue. Created a separate topic Nginx reverse proxy stuck at "Loading OctoPrint's UI, please wait..."

I stumpled across this, coming from Unable to display OctoPrint inside Home Assistant iframe

haproxy example from first post is deprecated, for reference my current version:

backend octoprint
  # ref https://community.octoprint.org/t/reverse-proxy-configuration-examples/
  http-request replace-path ^([^\ :]*)\ /local_octoprint_proxy/(.*)  \1\ /\2
  http-request add-header X-Script-Name %[req.hdr(X-Ingress-Path)]
  option forwardfor
  server octoprint OCTOPRINT_HOST:OCTOPRINT_PORT USE_SSL VERIFY_SSL check

The http-request add-header X-Script-Name is the most important.

I run some small haproxy against the octopi image which also runs some haproxy. Because, why not.

Here is a basic Caddy Server v2 configuration, assuming you are running Caddy on the same machine as the OctoPrint. It is easy to adapt if your OctoPrint installation is on another machine. The example is in the "Caddyfile" style.

localhost

reverse_proxy 127.0.0.1
header_up X-Scheme {scheme}

That's it.

In my specific case, I have a subdomain assigned to the OctoPrint installation, which is running on another machine from Caddy Server. My configuration is thus:

subdomain.example.com {
    reverse_proxy 192.168.0.100 {
    header_up X-Scheme {scheme}
    }
}

Because I have not specified http at the beginning of subdomain.example.com, the default is assumed to be https, and it will automatically generate a Let's Encrypt certificate and attach it by default.

1 Like

I was looking for a Caddy example to replace the haproxy.cfg (to use Caddy instead of HAProxy). It's surprisingly simple:

host.domain:80 {
        reverse_proxy localhost:5000
        handle_path /webcam* {
                reverse_proxy localhost:8080
        }
}

47 lines of haproxy.cfg are replaced with 6 lines of Caddyfile. And if you want TLS, it's just a few lines more (if this were exposed to the Internet, which it shouldn't be, you'd get TLS by just getting rid of :80 on the first line--but if it's internal, you'd need to configure for DNS validation, or use Caddy's internal CA).

1 Like

I have added octoprint to my existing nginx reverse proxy.
I generally followed the first post and it was successful.
However, in the octoprint settings, I had to add a period to the beginning of the Stream URL in the webcam.
As follows.
./webcam/?action=stream

PSA: haproxy 2.2.x (included in Debian 11 "Bullseye") no longer supports the reqrep configuration keyword. Instead, it uses http-request replace-path. The above haproxy.cfg needs to have the

reqrep ^([^\ :]*)\ /webcam/(.*)     \1\ /\2

line in the backend webcam section replaced with

http-request replace-path /webcam/(.*)   /\1

If you're upgrading a 0.18 or earlier version of OctoPi to Bullseye (or just upgrading haproxy to 2.2.x) then as well as the change above you also need to remove the reqrep line in the backend octoprint section in the default OctoPi configuration file. There is no need to add a new http-request line in the backend octoprint section to replace it.

4 Likes

Thank you @MMcLure, you got me pointed in the right direction. After updating the OctoPi 0.18.0 image from Buster to Bullseye I had to correct my HAProxy.cfg to the below setup. Only required me to make changes to anything related to reqrep or reqadd.

global
	maxconn 4096
	user haproxy
	group haproxy
	daemon
	log 127.0.0.1 local0 debug
	tune.ssl.default-dh-param 2048

defaults
	log     global
	mode    http
	compression algo gzip
	option  httplog
	option  dontlognull
	retries 3
	option redispatch
	option http-server-close
	option forwardfor
	maxconn 2000
	timeout connect 5s
	timeout client  15min
	timeout server  15min

frontend public
	bind *:80 v4v6
	bind *:443 v4v6 ssl crt /etc/ssl/snakeoil.pem
	option forwardfor except 127.0.0.1
	use_backend webcam if { path_beg /webcam/ }
	use_backend webcam_hls if { path_beg /hls/ }
	use_backend webcam_hls if { path_beg /jpeg/ }
	default_backend octoprint

backend octoprint
	acl needs_scheme req.hdr_cnt(X-Scheme) eq 0

	http-request set-header X-Forwarded-Proto https if { ssl_fc }
	http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
	option forwardfor
	server octoprint1 127.0.0.1:5000
	errorfile 503 /etc/haproxy/errors/503-no-octoprint.http

backend webcam
	http-request replace-path /webcam/(.*)   /\1
	server webcam1  127.0.0.1:8080
	errorfile 503 /etc/haproxy/errors/503-no-webcam.http

backend webcam_hls
	server webcam_hls_1 127.0.0.1:28126
	errorfile 503 /etc/haproxy/errors/503-no-webcam-hls.http

I have spent hours looking for this answer, thank you @MMcLure!

@Evan_Adventures I think you're looking for the example posted above by MMcLure.

I just spent a while getting octoprint to work using Nginx Proxy Manager docker image 1.26.0 and wanted to share my findings. I am using a wildcard certificate from the SecureTrust CA on nginx to plain-text HTTP on the octoprint side.

I was getting the dreaded websocket errors. I finally figured out that if I used "$scheme" for the X-Scheme header, the scheme would not be set properly. Hard coding this to "https" instead fixed that problem. I was also having a problem where occasionally, the webserver would redirect to the hostname with a comma and the hostname again. (e.g. octoprint.example.com,octoprint.example.com) This caused total failure. Sometimes, it would only happen in Safari, and other times, only in Chrome.

I finally got it figured out, and was able to setup both of my Octoprint instances behind nginx using Nginx Proxy Manager docker image 1.26.0 without any further issues. Here is what I recommend for using Nginx Proxy Manager on docker:

  • Add a new proxy host
  • Enter in the IP address and port of your Octoprint instance
  • Enable "Websockets Support" (This adds the http_version, upgrade, and connection headers)
  • Add a custom location for "/" using the same IP address and port of your octoprint instance
  • Click the gear to enable the custom config text box
  • Enter in the following:
proxy_set_header X-Scheme https;
client_max_body_size 0; 
  • Select the SSL tab and configure SSL
  • Click save

That should do it. Using "https" as the scheme was the lynchpin for getting the websockets to work, and removing the "Host" header from the custom location fixed the redirect with the extra comma. If you are not using SSL in nginx, then you should omit the "X-Scheme" configuration line.

Many of the options that are recommended are automatically added to the config by Nginx Proxy Manager, and for whatever reason, adding them again causes issues.

You can also view the full configuration file that is created by looking where docker stores the files for the container that Nginx Proxy Manager is running in. Here is the proxy host config file for my working setup:

server {
  set $forward_scheme http;
  set $server         "192.168.2.92";
  set $port           80;

  listen 8080;
listen [::]:8080;
listen 4443 ssl http2;
listen [::]:4443 ssl http2;
  server_name octoprint.example.com;
  # Custom SSL
  ssl_certificate /data/custom_ssl/npm-1/fullchain.pem;
  ssl_certificate_key /data/custom_ssl/npm-1/privkey.pem;

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_http_version 1.1;

  access_log /data/logs/proxy-host-5_access.log proxy;
  error_log /data/logs/proxy-host-5_error.log warn;

  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Forwarded-For    $remote_addr;
    proxy_set_header X-Real-IP		$remote_addr;
    proxy_pass       http://192.168.2.92:80;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme https;
client_max_body_size 0;
  }
  # Custom
  include /data/nginx/custom/server_proxy[.]conf;
}

Screenshots from Nginx Proxy Manager available at: https://imgur.com/a/SOKWdNx

I hope that helps someone.

2 Likes

I've been trying to use this but Im ending up in loop at the login scren, have you made any updates to your config since you posted this by chance?

Im trying to get just the webcam view to proxy out (so others can only see whats printing, but not see the whole octoprint controls, etc).

I run an apache webserver (hosting a few websites for friends/etc), which sits on both the public and private networks (dual homed/NICs).
I went to my 1 personal site config and added to its virt config:

ProxyPreserveHost On
ProxyPass /whatsprinting http://192.168.IP.ADDRESS/webcam/?action=stream
ProxyPassReverse /whatsprinting http://192.168.IP.ADDRESS/webcam/?action=stream

(of course with the real IP of my octoprint Pi in place of "IP.ADDRESS"...)

When I go to the private IP stream URL (on same network), it works fine, I see just the camera view.
When I go to http://www.k5ktf.com/whatsprinting (you could see it if you go there) I get

Snapshot
Refresh the page to refresh the snapshot
Snapshot
Stream
Stream

Is there something in the httpd.conf or octoprint config I need to add/change?

Thanks
Jim

Nevermiiiiind....
I found the issue...

For anyone else pulling hair out, you just use a rewrite rule with the [P] proxy flag instead of ProxyPass:

RewriteEngine On
RewriteRule ^/whatsprinting http://192.168.IP.ADDRESS/webcam/?action=stream [P]
#ProxyRequests Off
#ProxyPreserveHost On
#ProxyPass /whatsprinting http://192.168.IP.ADDRESS/webcam/?action=stream
#ProxyPassReverse /whatsprinting http://192.168.IP.ADDRESS/webcam/?action=stream

(the lines beginning with # are commented out of course, and will be removed from my config)

Thanks!
Jim

I'd like to share my traefik example configuration, as i'm running many docker containers on a separate raspberry and using traefik there as a reverse proxy with basic authentication for access to octopi (for the Octoapp). This is stored as a traefik file configuration. I also added a ratelimit of averaged 3 requests per second and 10 for a burst of time (traefik docs doesn't specify the timeframe what a "burst of time" is).

http:
  middlewares:
    octopi_auth:
      basicAuth:
        users:
          - "octopi:bcrypt_hash"
    octopi_rate_limit:
      rateLimit:
        average: 3
        burst: 10

  routers:
    octopi:
      rule: "Host(`octopi.example.com`)"
      entryPoints:
        - "websecure"
        - "web"
      service: "octopi_service"
      middlewares:
        - "octopi_auth"
        - "octopi_rate_limit"
      tls:
        certResolver: letsencrypt


  services:
    octopi_service:
      loadBalancer:
        servers:
          - url: "http://octopi.internal.local"
        passHostHeader: true

Hi,

I've got my Nginx reverse proxy all setup and working with the webcam location as well, but am concerned that anyone can access my webcam externally by going to https://myserver.com/webcam/?action=stream. Is there a way to block external access to the /webcam location while still allowing it to work with Octoprint while using Octoprint externally?

Yes, quite simple actually: Don't make your OctoPrint instance accessible on the public and hostile internet where every script kiddie with a Kali Linux instance can find you on Shodan and then attempt to break in or launch a fun DDOS attack on you. See also this post. Use a VPN. At the very least use basic auth and access restriction by IP.