Accessing erpnext with https on localhost [Using Caddy]

Greetings all,
I am using Erpnext locally with a team and have used Podman for deployment. I am using Caddy for reverse proxy for other systems, and everything works fine, but can I use Caddy for Erpnext? Is there any additional configuration I should do in the container or docker-compose file?

This is what how I am trying to access erpnext with https locally with the Caddy configuration.

erp.localdev.net {

  tls internal

  reverse_proxy localhost:8085 {
    transport http {
      tls
      tls_insecure_skip_verify
    }
  }
}

What may not work:

  • serving site, frappe needs host or X-Frappe-Site-Name header to identify which site to serve.
  • websockets may disconnect, special configuration required to keep connection alive for sockets to work.
1 Like

Yeah, so is there any other way to use self-signed certificates? just not to see warnings on browsers.

Hello @revant_one, I just found this tool GitHub - smallstep/certificates: 🛡️ A private certificate authority (X.509 & SSH) & ACME server for secure automated certificate management, so you can use TLS everywhere & SSO for SSH.
If I generate private and public keys will it work? I am checking out this documentation
Configuring HTTPS

I have already generated both private and public keys and trying to move it to nginx directory

  • let caddy handle the tls.
  • traefik is handling in easy-install.py script. Traefik does it using config and labels.
  • no need to mount or add files to frappe’s nginx container. Think of it as part of application and not part of infrastructure.

It does it without touching the application, refer here certificates/examples/docker/docker-compose.yml at master · smallstep/certificates · GitHub

you’ll need to customize as per your advance usage and tools.

To customize nginx config for “frontend”:

  1. use env variables
  2. or mount custom template at location /templates/nginx/frappe.conf.template, refer existing template here: frappe_docker/resources/nginx-template.conf at main · frappe/frappe_docker · GitHub

Yeah, but that is a docker-compose to run step-ca, a process that acts as Certificate Authority. For now, I have just created both private and public keys, which should be imported to the Erpnext webserver somehow, which is Nginx, I think.

Caddy was even easier since there will be no key exchanges, but I am getting an error on caddy and the page won’t load.

Dec 12 15:27:36 fedora caddy[1437]: {"level":"error","ts":1734006456.8929186,"logger":"http.log.error","msg":"tls: first record does not look like a TLS handshake","request":{"remote_ip":"192.168.4.33","remote_port":"48434","client_ip":"192.168.4.33","proto":"HTTP/2>

In my experience, the other application should have a configuration to connect with Caddy, but I couldn’t find any hint on erpnext.

If there’s any caddy user, they may help.

If you figure out, post somewhere caddy users read.

1 Like

Hello @revant_one
I posted the same topic on the caddy community and didn’t get a response, so I proceeded with generating a private key and certificate. I added the root CA to the trusted store on my localhost. Following this guide, I just placed those two inside /etc/nginx/conf.d/ssl.

I executed bench setup nginx, and I could confirm the certificate path is added to nginx.conf file. But I am getting the following error on a browser when I am trying to access the erpnext site with my domain (https://myerp.home.net:8085)

SSL_ERROR_RX_RECORD_TOO_LONG

Here is the content of nginx.conf file from frappe-bench/config/nginx.conf:

upstream frappe-bench-frappe {
	server 127.0.0.1:8000 fail_timeout=0;
}

upstream frappe-bench-socketio-server {
	server 127.0.0.1:9000 fail_timeout=0;
}



# setup maps


# server blocks



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

	server_name
		myerp.home.net
		;

	root /home/frappe/frappe-bench/sites;

	

	proxy_buffer_size 128k;
	proxy_buffers 4 256k;
	proxy_busy_buffers_size 256k;

	
	ssl_certificate      /etc/nginx/conf.d/ssl/certificate_bundle.crt;
	ssl_certificate_key  /etc/nginx/conf.d/ssl/private.key;
	ssl_session_timeout  5m;
	ssl_session_cache shared:SSL:10m;
	ssl_session_tickets off;
	ssl_stapling on;
	ssl_stapling_verify on;
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
	ssl_ecdh_curve secp384r1;
	ssl_prefer_server_ciphers on;
	

	add_header X-Frame-Options "SAMEORIGIN";
	add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
	add_header X-Content-Type-Options nosniff;
	add_header X-XSS-Protection "1; mode=block";
	add_header Referrer-Policy "same-origin, strict-origin-when-cross-origin";

	location /assets {
		try_files $uri =404;
		add_header Cache-Control "max-age=31536000";
	}

	location ~ ^/protected/(.*) {
		internal;
		try_files /$host/$1 =404;
	}

	location /socket.io {
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_set_header X-Frappe-Site-Name $host;
		proxy_set_header Origin $scheme://$http_host;
		proxy_set_header Host $host;

		proxy_pass http://frappe-bench-socketio-server;
	}

	location / {

 		rewrite ^(.+)/$ $1 permanent;
  		rewrite ^(.+)/index\.html$ $1 permanent;
  		rewrite ^(.+)\.html$ $1 permanent;

		location ~* ^/files/.*.(htm|html|svg|xml) {
			add_header Content-disposition "attachment";
			try_files /$host/public/$uri @webserver;
		}

		try_files /$host/public/$uri @webserver;
	}

	location @webserver {
		proxy_http_version 1.1;
		proxy_set_header X-Forwarded-For $remote_addr;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Frappe-Site-Name $host;
		proxy_set_header Host $host;
		proxy_set_header X-Use-X-Accel-Redirect True;
		proxy_read_timeout 120;
		proxy_redirect off;

		proxy_pass  http://frappe-bench-frappe;
	}

	# error pages
	error_page 502 /502.html;
	location /502.html {
		root /usr/local/lib/python3.11/site-packages/bench/config/templates;
		internal;
	}

	access_log  /var/log/nginx/access.log main;
	error_log  /var/log/nginx/error.log;

	# optimizations
	sendfile on;
	keepalive_timeout 15;
	client_max_body_size 50m;
	client_body_buffer_size 16K;
	client_header_buffer_size 1k;

	# enable gzip compresion
	# based on https://mattstauffer.co/blog/enabling-gzip-on-nginx-servers-including-laravel-forge
	gzip on;
	gzip_http_version 1.1;
	gzip_comp_level 5;
	gzip_min_length 256;
	gzip_proxied any;
	gzip_vary on;
	gzip_types
		application/atom+xml
		application/javascript
		application/json
		application/rss+xml
		application/vnd.ms-fontobject
		application/x-font-ttf
		application/font-woff
		application/x-web-app-manifest+json
		application/xhtml+xml
		application/xml
		font/opentype
		image/svg+xml
		image/x-icon
		text/css
		text/plain
		text/x-component
		;
		# text/html is always compressed by HttpGzipModule
}

# http to https redirect
	server {
		listen 80;
		server_name
			erp.liyumfi.net
			;

		return 301 https://$host$request_uri;
	}



	



There is another Nginx.conf found in /etc/nginx/nginx.conf I didn’t create that but I don’t know if that is causing an issue, any hint on this?

UPDATE
I just remember I didn’t set anything in SITES and FRAPPE_SITE_NAME_HEADER options.

I read your comment here on the related topic

update the SITES variable again with list of sites quoted in ` and separated by ,

SITES=`a.com`,`b.com`

labels need to change

Any guide out there is assuming you’re using a VM and not containers.

Frappe/frappe_docker is the only source for anything related to containers.

Recently I posted somewhere about overriding nginx config using the template. Frappe/erpnext deploy with nomad: proxy/upstream not working - #2 by revant_one.

Check the nginx-entrypoint.sh as it generates the nginx config using the .template file. You can override the template or the file directly.

@revant_one Finally, it worked for me with Caddy. I removed the following section, and it worked for me. I couldn’t find a way to make sure erpnext listens on port 443, so I edited the Caddy configuration file.

   transport http {
      tls
      tls_insecure_skip_verify
    }

Here is the final config file on caddy

erp.localdev.net {

  tls internal

  reverse_proxy localhost:8085 {

  }
}