Seeking guidance getting simple Volto-frontend install working behind nginx TLS/SSL

[In a subsequent message I posted a new version of the configuration described in this message that solves the problem described here.]

Ok! I’ve got a Plone 6.0.0rc1 site fully operational behind https/TLS, though there’s one minor glitch left with which I could use help. I’m including below a variation of the configuration I’m using, based on the nginx, Frontend, Backend container example, because others might find it useful (I would have!), plus it will make it easy for those in the know to guide me in addressing the remaining problem.

The problem is a momentary “Connection Refused” message in the content area of pages when I visit them using an external link. By “momentary” I mean that the proper content replaces the “Connection Refused” message almost immediately after that message is displayed. This does not otherwise occur, eg when navigating from page to page using links on the site.

You can currently see the behavior by visiting https://do.myriadicity.net.

I expect this is related to Volto’s two-stage page loading process. Unfortunately, I am not clear about the specifics of that process nor found a concise explanation and haven’t made any headway trying debug my configuration. I suspect that someone familiar with the process will quickly recognize the cause. If you do, I would appreciate it very much if you would steer me in the right direction!

Here’s my setup. It’s directly derived from nginx, Frontend, Backend container example, with the addition of LetsEncrypt certs established in the host file system and brought to the docker-composed nginx instance using data volumes. I also use data volumes for the backend storage for easy external access, for persistence and migration.

  1. Here’s my adapted docker-compose.yml file. The crucial difference is the setting for RAZZLE_INTERNAL_API_PATH. I suspect that the problem I mention above is because I haven’t got this setting quite right, but as I said everything else does work. (I also changed the backend version to Plone 6.0.0rc1. Docker compose makes it extremely easy to update stuff!)

    version: "3"
    services:
    
      webserver:
        image: nginx
        volumes:
          - ./default.conf:/etc/nginx/conf.d/default.conf
          - /etc/letsencrypt:/etc/letsencrypt
        depends_on:
          - backend
          - frontend
        ports:
          - "80:80"
          - "443:443"
    
      frontend:
        image: plone/plone-frontend:latest
        environment:
          RAZZLE_INTERNAL_API_PATH: http://backend/Plone
          CORS_ALLOW_ORIGIN: "*"
          CORS_ALLOW_CREDENTIALS: "true"
        ports:
          - "3000:3000"
        depends_on:
          - backend
    
      backend:
        image: plone/plone-backend:6.0.0rc1
        environment:
          SITE: Plone
        volumes:
          - /var/local/data/trial/filestorage:/data/filestorage
          - /var/local/data/trial/blobstorage:/data/blobstorage
        ports:
          - "8080:8080"
    
    volumes:
      data: {}
    
  2. Here’s my adapted nginx default.conf, implementing ssl and forwarding port 80 access to ssl. It uses www.example.com for the website hostname.

    upstream backend {
      server backend:8080;
    }
    upstream frontend {
      server frontend:3000;
    }
    
    server {
      server_name www.example.com;
    
      listen 443 ssl; # managed by Certbot
      ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; # managed by Certbot
      ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; # managed by Certbot
      include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
      ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
      location ~ /\+\+api\+\+($|/.*) {
          #error_log /home/klm/scratch/myrerrlog notice;
          rewrite ^/(\+\+api\+\+\/?)+($|/.*) /VirtualHostBase/https/$server_name/Plone/++api++/VirtualHostRoot/$2 break;
          proxy_pass http://backend;
      }
    
      location ~ / {
          location ~* \.(js|jsx|css|less|swf|eot|ttf|otf|woff|woff2)$ {
              add_header Cache-Control "public";
              expires +1y;
              proxy_pass http://frontend;
          }
          location ~* static.*\.(ico|jpg|jpeg|png|gif|svg)$ {
              add_header Cache-Control "public";
              expires +1y;
              proxy_pass http://frontend;
          }
          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 http:// https://;
          proxy_pass http://frontend;
      }
    }
    
    server {
    
      listen 80 default_server;
      server_name www.example.com;
      location ~ /\+\+api\+\+($|/.*) {
          rewrite ^/(\+\+api\+\+\/?)+($|/.*) /VirtualHostBase/http/$server_name/Plone/++api++/VirtualHostRoot/$2 break;
          proxy_pass http://backend;
          }
       if ($host = www.example.com) {
           return 301 https://$host$request_uri;
       } # managed by Certbot
       return 404; # managed by Certbot
    }
    

Some more possibly useful notes:

  • I’m using data volumes to share the LetsEncrypt installed and updated certs with the containerized nginx install. I will need to tweak the LetsEncrypt renewed-cert procedure to restart that containerized nginx install, using something like cd dockercompose-dir && docker compose restart webserver.

  • Alternatively, there’s a very promising repository offering a containerized packaging of the LetsEncrypt + nginx facilities: evgeniy-khist/letsencrypt-docker-compose: Nginx and Let’s Encrypt with Docker Compose in less than 3 minutes (github.com)

  • During initial docker composition of the “Plone Volto site: Plone” a banner message includes “THIS IS NOT MEANT TO BE USED IN PRODUCTION”. I would like to know more about why that is, and whether I should rethink basing my site’s install on all this. The page referenced with a link includes this warning: We advise against using this feature on production environments., (in reference to a docker run command), but isn’t particularly illuminating.