NextCloud – Using FPM Docker Image Behind Nginx Reverse Proxy

dockernextcloudnginx-reverse-proxy

The Issue – I'm getting a 404 – File not Found when trying to access the App (Including First Time)

When trying to access the App on a browser via nextcloud.example.com I'm getting this Error from the Console Log of the NextCloud FMP Docker Container:

172.19.0.5 -  04/Mar/2023:22:36:01 +0000 "GET /index.php" 404

And this error from the Console Log of the NGinX Docker Container:

172.69.67.108 - - [04/Mar/2023:22:36:01 +0000] "GET / HTTP/2.0" 404 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" "xxx.my.ip.xxx"

The setup of the Docker Server Host and the Containers

  1. Docker Host Server OS is Ubuntu 22.04 LTS

  2. NGinX Docker Container, I am aware that a docker-compose.yml is best, but for now and until I get this working properly, I'm running the Container with:

    docker run -p 80:80 -p 443:443 --name nginx \
        --restart=unless-stopped \
        -v certbot:/etc/letsencrypt \
        -v nginx:/etc/nginx/conf.d \
        -v www:/usr/share/nginx/html \
        --network tools \
        -d nginx
    

    This is the only Container on the Docker Server Host with Ports Exposed and uses volume mounts to share:

    • Certificates with a CertBot Container
    • NGinX Conf Directory to set up a conf for each app
    • HTML www root home to share with the NextCloud FPM Docker Container
    • Network Tools
  3. NextCloud FPM Docker Container, I'm running the Container with:

    docker run --name nextcloud-fpm \
        --restart=unless-stopped \
        -v www:/var/www/html \
        -e 'TRUSTED_PROXIES=nginx' \
        -e 'POSTGRES_HOST=database.server.com' \
        -e 'POSTGRES_USER=database_user' \
        -e 'POSTGRES_PASSWORD=database_user_password' \
        -e 'POSTGRES_DB=databse_name' \
        --network tools \
        -d nextcloud:fpm
    

    This Container is run on the same Network Tools, to not have to expose the app's Ports to the Docker Host Server, and to make it easier when it's time to create the Container Stacks and NGinX conf file by using the Container Names instead of IPs for Proxy Reverses

    • HTML www root home to share with the NGinX Docker Container
    • Environmental Variables for Trusted Proxy (the NGinX Container
    • Environmental Variables for PostgreSQL Database
    • This Cotainer runs FPM on Port 9000
    • Network Tools
  4. NGinX conf Files, there are 2 of them, one default.conf to mainly not allow the Host Server to be reached by IP directly and port (Example xxx.xxx.xxx.xxx:80 or xxx.xxx.xxx.xxx:443):

    server {
        # Removes Web Server Info
        server_tokens off;
        # HTTP
        listen      80 default_server;
        listen [::]:80 default_server;
        listen      443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;
        ssl_reject_handshake on;
        # Identify Server without the name, hence just IP
        server_name _;
        # Return Empty Response
        return 444;
    }
    

    Finally the nextcloud.conf, it was taken form the GitHub NextCloud FPM Official Repo:

    upstream php-handler {
        server nextcloud-fpm:9000; # In here I set up the Stream with the NextCloud FPM Container name
    }
    
    server {
    listen 80;
    listen [::]:80;
    server_name nextcloud.example.com;
    
    return 301 https://$server_name$request_uri;
    }
    
    server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name nextcloud.example.com;
    
    ssl_certificate /etc/letsencrypt/live/nextcloud.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/nextcloud.example.com/privkey.pem;
    
        # HSTS settings
        # 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 Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
    
        # set max upload size
        client_max_body_size 512M;
        fastcgi_buffers 64 4K;
    
        # Enable gzip but do not remove ETag headers
        gzip on;
        gzip_vary on;
        gzip_comp_level 4;
        gzip_min_length 256;
        gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
        gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/>
        # Pagespeed is not supported by Nextcloud, so if your server is built
        # with the `ngx_pagespeed` module, uncomment this line to disable it.
        #pagespeed off;
    
        # HTTP response headers borrowed from Nextcloud `.htaccess`
        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;
    
        # Remove X-Powered-By, which is an information leak
        fastcgi_hide_header X-Powered-By;
    
        # Path to the root of your installation
        root /usr/share/nginx/html/; # Since NGinX is the Web Server, I am pointing root to where the NextCloud Files are mounted inside the NGinX Container from the NextCloud FPM Container
    
        # Specify how to handle directories -- specifying `/index.php$request_uri`
        # here as the fallback means that Nginx always exhibits the desired behaviour
        # when a client requests a path that corresponds to a directory that exists
        # on the server. In particular, if that directory contains an index.php file,
        # that file is correctly served; if it doesn't, then the request is passed to
        # the front-end controller. This consistent behaviour means that we don't need
        # to specify custom rules for certain paths (e.g. images and other assets,
        # `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
        # `try_files $uri $uri/ /index.php$request_uri`
        # always provides the desired behaviour.
        index index.php index.html /index.php$request_uri;
    
        # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
        location = / {
            if ( $http_user_agent ~ ^DavClnt ) {
                return 302 /remote.php/webdav/$is_args$args;
            }
        }
    
        location = /robots.txt {
            allow all;
            log_not_found off;
            access_log off;
        }
    
        # Make a regex exception for `/.well-known` so that clients can still
        # access it despite the existence of the regex rule
        # `location ~ /(\.|autotest|...)` which would otherwise handle requests
        # for `/.well-known`.
        location ^~ /.well-known {
            # The rules in this block are an adaptation of the rules
            # in `.htaccess` that concern `/.well-known`.
    
            location = /.well-known/carddav { return 301 /remote.php/dav/; }
            location = /.well-known/caldav  { return 301 /remote.php/dav/; }
    
            location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
            location /.well-known/pki-validation    { try_files $uri $uri/ =404; }
    
            # Let Nextcloud's API for `/.well-known` URIs handle all other
            # requests by passing them to the front-end controller.
            return 301 /index.php$request_uri;
        }
    
        # Rules borrowed from `.htaccess` to hide certain paths from clients
        location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
        location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }
    
        # Ensure this block, which passes PHP files to the PHP process, is above the blocks
        # which handle static assets (as seen below). If this block is not declared first,
        # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
        # to the URI, resulting in a HTTP 500 error response.
        location ~ \.php(?:$|/) {
            # Required for legacy support
            rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
    
            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
            set $path_info $fastcgi_path_info;
    
            try_files $fastcgi_script_name =404;
    
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $path_info;
            #fastcgi_param HTTPS on;
    
            fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
            fastcgi_param front_controller_active true;     # Enable pretty urls
            fastcgi_pass php-handler;
    
            fastcgi_intercept_errors on;
            fastcgi_request_buffering off;
        }
    
        location ~ \.(?:css|js|svg|gif)$ {
            try_files $uri /index.php$request_uri;
            expires 6M;         # Cache-Control policy borrowed from `.htaccess`
            access_log off;     # Optional: Don't log access to assets
        }
    
        location ~ \.woff2?$ {
            try_files $uri /index.php$request_uri;
            expires 7d;         # Cache-Control policy borrowed from `.htaccess`
            access_log off;     # Optional: Don't log access to assets
        }
    
        # Rule borrowed from `.htaccess`
        location /remote {
            return 301 /remote.php$request_uri;
        }
    
        location / {
            try_files $uri $uri/ /index.php$request_uri;
        }
    }
    

What I have tried so far

  1. My First instinct was to check the Logs, I shared them at the start of this post, and based on that I went to check if the Files were Present on both Containers, I did this in two different ways:

    I have a Container of Portainer Running on the same Docker Host Server and also behind the sane NGinX Docker Container as Proxy Reverse, I attached a console and look for the files inside both Containers, also did docker exec container_name -it bash on both containers:

    NGinX Container, remember the mount was on /usr/share/nginx/html

    root@61564cff4e67:/# ls -lha /usr/share/nginx/html
    total 180K
    drwxrwxrwx 15 www-data www-data 4.0K Mar  4 13:24 .
    drwxr-xr-x  3 root     root     4.0K Feb  9 04:36 ..
    -rw-r--r--  1 www-data www-data 3.2K Mar  4 13:24 .htaccess
    -rw-r--r--  1 www-data www-data  101 Mar  4 13:24 .user.ini
    drwxr-xr-x 47 www-data www-data 4.0K Mar  4 13:24 3rdparty
    -rw-r--r--  1 www-data www-data  19K Mar  4 13:24 AUTHORS
    -rw-r--r--  1 www-data www-data  34K Mar  4 13:24 COPYING
    drwxr-xr-x 50 www-data www-data 4.0K Mar  4 13:24 apps
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 config
    -rw-r--r--  1 www-data www-data 4.0K Mar  4 13:24 console.php
    drwxr-xr-x 23 www-data www-data 4.0K Mar  4 13:24 core
    -rw-r--r--  1 www-data www-data 6.2K Mar  4 13:24 cron.php
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 custom_apps
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 data
    drwxr-xr-x  2 www-data www-data  12K Mar  4 13:24 dist
    -rw-r--r--  1 www-data www-data  156 Mar  4 13:24 index.html
    -rw-r--r--  1 www-data www-data 3.4K Mar  4 13:24 index.php
    drwxr-xr-x  6 www-data www-data 4.0K Mar  4 13:24 lib
    -rwxr-xr-x  1 www-data www-data  283 Mar  4 13:24 occ
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 ocm-provider
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 ocs
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 ocs-provider
    -rw-r--r--  1 www-data www-data 3.1K Mar  4 13:24 public.php
    -rw-r--r--  1 www-data www-data 5.5K Mar  4 13:24 remote.php
    drwxr-xr-x  4 www-data www-data 4.0K Mar  4 13:24 resources
    -rw-r--r--  1 www-data www-data   26 Mar  4 13:24 robots.txt
    -rw-r--r--  1 www-data www-data 2.4K Mar  4 13:24 status.php
    drwxr-xr-x  3 www-data www-data 4.0K Mar  4 13:24 themes
    -rw-r--r--  1 www-data www-data  383 Mar  4 13:24 version.php
    

    NextCloud FPM Container, remember the mount was on /var/www/html

    root@938c95d0e1ae:/var/www/html# ls -lha
    total 180K
    drwxrwxrwx 15 www-data www-data 4.0K Mar  4 13:24 .
    drwxrwxr-x  1 www-data root     4.0K Feb 16 03:06 ..
    -rw-r--r--  1 www-data www-data 3.2K Mar  4 13:24 .htaccess
    -rw-r--r--  1 www-data www-data  101 Mar  4 13:24 .user.ini
    drwxr-xr-x 47 www-data www-data 4.0K Mar  4 13:24 3rdparty
    -rw-r--r--  1 www-data www-data  19K Mar  4 13:24 AUTHORS
    -rw-r--r--  1 www-data www-data  34K Mar  4 13:24 COPYING 
    drwxr-xr-x 50 www-data www-data 4.0K Mar  4 13:24 apps
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 config
    -rw-r--r--  1 www-data www-data 4.0K Mar  4 13:24 console.php
    drwxr-xr-x 23 www-data www-data 4.0K Mar  4 13:24 core
    -rw-r--r--  1 www-data www-data 6.2K Mar  4 13:24 cron.php
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 custom_apps
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 data
    drwxr-xr-x  2 www-data www-data  12K Mar  4 13:24 dist
    -rw-r--r--  1 www-data www-data  156 Mar  4 13:24 index.html
     -rw-r--r--  1 www-data www-data 3.4K Mar  4 13:24 index.php
    drwxr-xr-x  6 www-data www-data 4.0K Mar  4 13:24 lib
    -rwxr-xr-x  1 www-data www-data  283 Mar  4 13:24 occ
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 ocm-provider
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 ocs
    drwxr-xr-x  2 www-data www-data 4.0K Mar  4 13:24 ocs-provider
    -rw-r--r--  1 www-data www-data 3.1K Mar  4 13:24 public.php
    -rw-r--r--  1 www-data www-data 5.5K Mar  4 13:24 remote.php
    drwxr-xr-x  4 www-data www-data 4.0K Mar  4 13:24 resources
    -rw-r--r--  1 www-data www-data   26 Mar  4 13:24 robots.txt
    -rw-r--r--  1 www-data www-data 2.4K Mar  4 13:24 status.php
    drwxr-xr-x  3 www-data www-data 4.0K Mar  4 13:24 themes
    -rw-r--r--  1 www-data www-data  383 Mar  4 13:24 version.php
    

    Docker Server Host Bind Mount Lastly to check the files o the Docker Host Server itself, as I do not only want to achieve sharing the root location for both Containers also have persistent data if I need to move the Container:

    ❯ ll
    total 180K
    drwxrwxrwx 15 www-data  www-data  4.0K Mar  4 13:24 .
    drwxrwxr-x 10 Alejandro Alejandro 4.0K Mar  4 13:24 ..
    drwxr-xr-x 47 www-data  www-data  4.0K Mar  4 13:24 3rdparty
    drwxr-xr-x 50 www-data  www-data  4.0K Mar  4 13:24 apps
    -rw-r--r--  1 www-data  www-data   19K Mar  4 13:24 AUTHORS
    drwxr-xr-x  2 www-data  www-data  4.0K Mar  4 13:24 config
    -rw-r--r--  1 www-data  www-data  4.0K Mar  4 13:24 console.php
    -rw-r--r--  1 www-data  www-data   34K Mar  4 13:24 COPYING
    drwxr-xr-x 23 www-data  www-data  4.0K Mar  4 13:24 core
    -rw-r--r--  1 www-data  www-data  6.2K Mar  4 13:24 cron.php
    drwxr-xr-x  2 www-data  www-data  4.0K Mar  4 13:24 custom_apps
    drwxr-xr-x  2 www-data  www-data  4.0K Mar  4 13:24 data
    drwxr-xr-x  2 www-data  www-data   12K Mar  4 13:24 dist
    -rw-r--r--  1 www-data  www-data  3.2K Mar  4 13:24 .htaccess
    -rw-r--r--  1 www-data  www-data   156 Mar  4 13:24 index.html
    -rw-r--r--  1 www-data  www-data  3.4K Mar  4 13:24 index.php
    drwxr-xr-x  6 www-data  www-data  4.0K Mar  4 13:24 lib
    -rwxr-xr-x  1 www-data  www-data   283 Mar  4 13:24 occ
    drwxr-xr-x  2 www-data  www-data  4.0K Mar  4 13:24 ocm-provider
    drwxr-xr-x  2 www-data  www-data  4.0K Mar  4 13:24 ocs
    drwxr-xr-x  2 www-data  www-data  4.0K Mar  4 13:24 ocs-provider
    -rw-r--r--  1 www-data  www-data  3.1K Mar  4 13:24 public.php
    -rw-r--r--  1 www-data  www-data  5.5K Mar  4 13:24 remote.php
    drwxr-xr-x  4 www-data  www-data  4.0K Mar  4 13:24 resources
    -rw-r--r--  1 www-data  www-data    26 Mar  4 13:24 robots.txt
    -rw-r--r--  1 www-data  www-data  2.4K Mar  4 13:24 status.php
    drwxr-xr-x  3 www-data  www-data  4.0K Mar  4 13:24 themes
    -rw-r--r--  1 www-data  www-data   101 Mar  4 13:24 .user.ini
    -rw-r--r--  1 www-data  www-data   383 Mar  4 13:24 version.php
    
    root@myserver /mnt/Volume/data/www
    

    I see no reason why permission wise it would not work and have confirmed that the files are present in all three instances as they should.

  2. Check the NGinX conf file for NextCloud FPM nextcloud.conf, after a lot of consideration the only thing I think can be wrong since I'm reaching via the domain on a browser all the way to the NGniX Web Server Container, as I have 404 Not Found entries in its log and I'm also getting a response from the NextCloud Container instance itself a 404 Not Found as well, that the only problem I have is related to the root entry on the conf file:

    NextCloud Log:

    172.19.0.5 -  04/Mar/2023:22:36:01 +0000 "GET /index.php" 404
    

    NGinX Log:

    172.69.67.108 - - [04/Mar/2023:22:36:01 +0000] "GET / HTTP/2.0" 404 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" "xxx.my.ip.xxx"
    

    So I changed the root directive to be /var/www/html, and the behavior changes just a bit, I'm still getting a 404 Not Found entry but only on the NGinX Web Server Container logs, the NextCloud Container is silent, so I'm not reaching it with this change:

    NextCloud Log (empty no entry at all):

    
    

    NGinX Log:

    2023/03/05 00:13:24 [error] 473#473: *4152 "/var/www/html/index.php" is not found (2: No such file or directory), client: 172.69.65.73, server: nextcloud.example.com, request: "GET / HTTP/2.0", host: "nextcloud.example.com"
    172.69.65.73 - - [05/Mar/2023:00:13:24 +0000] "GET / HTTP/2.0" 404 174 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" "xxx.my.ip.xxx"
    

    There's something different in the way the Web Server behaves, I notice that after the root directive change, I see the actual domain between quotes on the log "nextcloud.example.com" when before was just a dash "-".

  3. Next thing was to just disable default.conf NGinX conf file, as is the only place I can think of the "-", same results as above and I repeated on both scenarios 1 and 2 I have already tried, it is a "_" on the server directive of the default.conf too so was a shot in the dark.

  4. I have tried many other settings with the conf but most of them end up being Too Many Redirections, Bad Certificate, or just plain 502 errors.

My Determination of What the Problem and Where I'm stuck

It seems I'm very close to my desired result, as I am reaching both Containers from browsers with the desired domain, and SSL is resolving fine as well, however, the server name doesn't seem to be captured by the NGinX conf file, and then not passed correctly to NextCloud, but I don't know what exactly to change.

Since I think I narrowed it down to what I think is the specific issue maybe someone can help me review the NGinX conf file nextcloud.conf and point out what is set wrong.

Thanks, for even reading this long post.

Best Answer

I figured it out, so I will give the detailed info to help people with the same problem already and for people that might run into this in the future, considering how many questions and issues I found about this without answers.

The Issue was the FPM root location in the FastCGI Parameters of the NGinX conf file

So I was on the right track, and the problem was tied to the root directive.

After much reading I stumbled upon this post:

How to correctly link php-fpm and Nginx Docker containers?

And suddenly it became clear I had two different services reading 2 different paths, and one was feeding the root path of the other one where it didn't exist.

Remember I had mounted the www volume to be shared between the 2 containers?

  1. For the NGinX Container on www:/usr/share/nginx/html
  2. For the NextCloud FPM Container on www:/var/html/www

The idea behind this was for the two containers to share the filesystem of the NextCloud app.

What I didn't realize is that in this deployment, NGinX's purpose is to serve the static files, like CSS, HTML, JPG, JS, etc., while routing PHP calls to the NextCloud FPM container with the FastCGI Parameters.

When the root directive in the NGinX conf file is declared, it gets set as the variable $document_root.

root /usr/share/nginx/html;

So when setting the root to the path on the NGinX Container /usr/share/nginx/html this works to serve the static files without issues, but then, when a PHP call is made it's fed to the php-hanlder in the NextCloud FPM Container, since that location doesn't exist inside the second container, it can't find the PHP file.

To fix this I searched for the block with the FastCGI Parameters and searched for the $document_root variable, found it, and looks like this:

location ~ \.php(?:$|/) {
            # Required for legacy support
            rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;

            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
            set $path_info $fastcgi_path_info;

            try_files $fastcgi_script_name =404;

            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $path_info;
            #fastcgi_param HTTPS on;

            fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
            fastcgi_param front_controller_active true;     # Enable pretty urls
            fastcgi_pass php-handler;

            fastcgi_intercept_errors on;
            fastcgi_request_buffering off;
        }

Note how the line that says fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; is forming the Script Path with the root already declared, so when processing nextcloud.example.com it tries to read index.php and this is the resulting call to the NextCloud FPM container:

/usr/share/nginx/html/index.php

The correct call would have been:

/var/www/html/index.php

So I created a new variable named $fpm_root and set it to /var/www/html/, then changed the line that says fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; to fastcgi_param SCRIPT_FILENAME $fpm_root$fastcgi_script_name;

Now whenever it gets a call for static content it will resolve NGinX's Container path and when it gets a call for PHP content the NextCloud FPM's Container path.

Here's how the whole section looks now:

location ~ \.php(?:$|/) {
            # Required for legacy support
            rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;

            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
            set $path_info $fastcgi_path_info;
            set $fpm_root /var/www/html/;

            try_files $fastcgi_script_name =404;

            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $fpm_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $path_info;
            fastcgi_param HTTPS on;

            fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
            fastcgi_param front_controller_active true;     # Enable pretty urls
            fastcgi_pass php-handler;

            fastcgi_intercept_errors on;
            fastcgi_request_buffering off;
        }

It works now and passes all the internal NextCloud health and security checks once I added the line:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Conclusion

And I hope this helps anyone searching for this issue, the problem was that I had two different paths for the services processing both types of content, static and PHP, and only one of them was properly set on the NGinX conf file.

You can have two different (or more) root locations for a dockerized FPM deployment, and this could probably do to work or deploy static content into two or more different apps using the same FPM container to handle PHP calls for NextCloud, WordPress, PHPPGAdmin, Laravel or whatever you are working on.

Related Topic