nginxdocker-composenextcloud

nginx proxy_pass with nextcloud 27 docker - Your web server is not properly set up to resolve "/ocm-provider/"


I've had nextcloud running through docker for quite a while. I'm not sure how long, but it's been at least 3 years.

nextcloud is running via docker compose and I'm using nginx to proxy the nextcloud docker http traffic to the internet. nginx is also proxying many other subdomains for other services, so using something else to forward 80/443 traffic would be way too much work.

Over the years, every once in a while I'll see a warning on the nextcloud Security & setup warnings page and they're normally easy to correct with some googling.

This latest warning wasn't obvious how to correct and after poking around in the documentation, I realized that the code-rot in my nginx config for nextcloud was quite putrid and decided I might as well update my nginx conf to what is recommended by nextcloud.

Nextcloud Warning:

Your web server is not properly set up to resolve "/ocm-provider/". This is most likely related to a web server configuration that was not updated to deliver this folder directly. Please compare your configuration against the shipped rewrite rules in ".htaccess" for Apache or the provided one in the documentation for Nginx at it's documentation page ↗. On Nginx those are typically the lines starting with "location ~" that need an update.

Nextcloud mentions that only Apache is officially supported, and their documentation is for running nextcloud on bare metal and not through docker. https://docs.nextcloud.com/server/27/admin_manual/installation/nginx.html Add on top of that I'm not using the office nextcloud docker image, I can't remember why but I'm sure there was a reason for the docker image I'm using.

I updated my nginx config piecemeal and commented out any code block that broke things more than they were. In the end I was able get my config to closely resemble their example config without breaking things further.

Below is my docker-compose.yml

version: '2'

networks:
  default:
    driver: bridge

services:
  nextcloud:
    image: ghcr.io/hoellen/nextcloud
    restart: unless-stopped
    depends_on:
      - nextcloud-db
      - redis
    environment:
      - UID=1000
      - GID=1000
      - UPLOAD_MAX_SIZE=10G
      - APC_SHM_SIZE=128M
      - OPCACHE_MEM_SIZE=128
      - CRON_PERIOD=15m
      - TZ=America/Vancouver
      - DOMAIN=cloud.redacted.xyz
      - DB_TYPE=mysql
      - DB_NAME=nextcloud
      - DB_USER=nextcloud
      - DB_PASSWORD=redacted1234
      - DB_HOST=nextcloud-db
    volumes:
      - ./volumes/nextcloud/data:/data
      - ./volumes/nextcloud/config:/nextcloud/config
      - ./volumes/nextcloud/apps:/nextcloud/apps2
      - ./volumes/nextcloud/themes:/nextcloud/themes
    ports:
      - 8888:8888

  nextcloud-db:
    image: mariadb:10.6
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --innodb-read-only-compressed=OFF
    volumes:
      - ./volumes/nextcloud/db:/var/lib/mysql
      - ./volumes/nextcloud/my.cnf:/etc/mysql/my.cnf
    environment:
      - MYSQL_ROOT_PASSWORD=redacted5678
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=redacted1234
      - TZ=America/Vancouver

  redis:
    image: redis:alpine
    restart: unless-stopped
    container_name: redis
    volumes:
      - ./volumes/nextcloud/redis:/data
    environment:
      - TZ=America/Vancouver

Here is my nginx nextcloud.conf

upstream php-handler {
    server 127.0.0.1:9000;
}

map $arg_v $asset_immutable {
    "" "";
    default "immutable";
}


server {
    listen 192.168.1.10:80;
    server_name cloud.redacted.xyz;

    server_tokens off;

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

server {
    listen 192.168.1.10:443 ssl http2; # managed by Certbot
    server_name cloud.redacted.xyz;

    ssl_certificate     /etc/letsencrypt/live/cloud.redacted.xyz/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/cloud.redacted.xyz/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

    server_tokens off;

    client_max_body_size 512M;
    client_body_timeout 300s;
    fastcgi_buffers 64 4K;

    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 text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    client_body_buffer_size 512k;

    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                      "noindex, nofollow" always;
    add_header X-XSS-Protection                  "1; mode=block"     always;

    fastcgi_hide_header X-Powered-By;

    include mime.types;
    types {
        text/javascript js mjs;
    }

    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;
    }

    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;
    }

    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }


#    location ~ \.php(?:$|/) {
        # Required for legacy support
#        rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-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;

#        fastcgi_max_temp_file_size 0;
#    }

    location ~ \.(?:css|js|mjs|svg|gif|png|jpg|ico|wasm|tflite|map)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463, $asset_immutable";
        access_log off;     # Optional: Don't log access to assets

        location ~ \.wasm$ {
            default_type application/wasm;
        }
    }

    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;
        proxy_pass        http://127.0.0.1:8888;

        proxy_hide_header X-XSS-Protection;
        add_header X-XSS-Protection "1; mode=block" always;
    }
}

If I uncomment location ~ \.php(?:$|/) { and the closing brace or the section

    location /remote {
        return 301 /remote.php$request_uri;
    }

then I'm receiving the following warnings in nextcloud

Your web server is not yet properly set up to allow file synchronisation, because the WebDAV interface seems to be broken. Your web server is not properly set up to resolve "/ocm-provider/". This is most likely related to a web server configuration that was not updated to deliver this folder directly. Please compare your configuration against the shipped rewrite rules in ".htaccess" for Apache or the provided one in the documentation for Nginx at it's documentation page ↗. On Nginx those are typically the lines starting with "location ~" that need an update. Your web server is not properly set up to resolve "/.well-known/webfinger". Further information can be found in the documentation ↗. Your web server is not properly set up to resolve "/.well-known/nodeinfo". Further information can be found in the documentation ↗. Your web server is not properly set up to resolve "/.well-known/caldav". Further information can be found in the documentation ↗. Your web server is not properly set up to resolve "/.well-known/carddav". Further information can be found in the documentation ↗.

If I only uncomment out try_files $uri $uri/ /index.php$request_uri; in location / { then I get a 500 Internal Server Error

ANY help would be very much appreciated.

Thanks for your time to ready my dribble, Dan

EDIT After some playing, I've corrected most of my code and it is very close to the original suggested code from nextcloud. I'm still getting the warning about ocm-provider, but I see an open ticket on the hoellen/nextcloud github page https://github.com/hoellen/docker-nextcloud/issues/47.

Below is what I'm currently using in my nginx config

upstream php-handler {
    server 127.0.0.1:8888;
    server unix:/var/run/php/php-fpm.sock;
}

map $arg_v $asset_immutable {
    "" "";
    default "immutable";
}


server {
    listen 192.168.1.10:80;
    server_name cloud.redacted.xyz;

    server_tokens off;

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

server {
    listen 192.168.1.10:443 ssl http2; # managed by Certbot
    server_name cloud.redacted.xyz;

    ssl_certificate     /etc/letsencrypt/live/cloud.redacted.xyz/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/cloud.redacted.xyz/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

    server_tokens off;

    client_max_body_size 512M;
    client_body_timeout 300s;
    fastcgi_buffers 64 4K;

    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 text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    client_body_buffer_size 512k;

    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                      "noindex, nofollow" always;
    add_header X-XSS-Protection                  "1; mode=block"     always;

    fastcgi_hide_header X-Powered-By;

    include mime.types;
    types {
        text/javascript js mjs;
    }

    index index.php index.html /index.php$request_uri;

    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;
    }

    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;
    }

    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }


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

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

        try_files $fastcgi_script_name =404 @proxy;

        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;

        fastcgi_max_temp_file_size 0;
    }

    location ~ \.(?:css|js|mjs|svg|gif|png|jpg|ico|wasm|tflite|map)$ {
        try_files $uri /index.php$request_uri @proxy;
        add_header Cache-Control "public, max-age=15778463, $asset_immutable";
        access_log off;     # Optional: Don't log access to assets

        location ~ \.wasm$ {
            default_type application/wasm;
        }
    }

    location ~ \.woff2?$ {
        try_files $uri /index.php$request_uri @proxy;
        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 @proxy;
    }

    location @proxy {
        proxy_pass        http://php-handler;

        proxy_hide_header X-XSS-Protection;
        add_header X-XSS-Protection "1; mode=block" always;
    }
}

Solution

  • I've managed to resolve this problem in my image, have a look at this Nginx config.

    For me these two clauses were critical:

            location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode\/proxy)\.php(?:$|\/) {
                include /etc/nginx/fastcgi_params;
                fastcgi_split_path_info ^(.+\.php)(/.*)$;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
                fastcgi_param modHeadersAvailable true;
                fastcgi_param front_controller_active true;
                fastcgi_pass unix:/php/run/php-fpm.sock;
                fastcgi_intercept_errors on;
                fastcgi_request_buffering off;
                fastcgi_read_timeout 1200;
            }
    
            location ~ ^\/(?:updater|ocs-provider)(?:$|\/) {
                try_files $uri/ =404;
                index index.php;
            }
    

    In both clauses you must change oc[ms] regex fragment to ocs.