Score:1

nginx configuration for nextcloud with path

cn flag

I run nextcloud on docker (image nextcloud:production-fpm) with an nginx-container (image: nginx) as reverse-proxy (handling SSL-termination) based on the example nginx-configuation (https://raw.githubusercontent.com/nextcloud/docker/master/.examples/docker-compose/insecure/mariadb/fpm/web/nginx.conf). This works.

Now however I want nextcloud to be served in an path (i.e. mydomain.com/nextcloud instead of mydomain). I found an github-issue on this (https://github.com/nextcloud/docker/issues/401) which point at the nginx-configuration, but this only solves it for very easy configurations (adapting the nextcloud by adding - OVERWRITEWEBROOT=/nextcloud to the environment is the easiest part). Does anybody now how to adapt the nginx-configuration to serve nextcloud from a path?

I created a demo-project to illustrate my problem: compose.yml:

version: '3'

volumes:
  nextcloud:
  mariadb:

services:
  mariadb:
    image: mariadb
    container_name: mariadb-subdir
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    volumes:
      - mariadb:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mariadb_root_password
      - MYSQL_PASSWORD_FILE=/run/secrets/mariadb_password
      - MYSQL_DATABASE_FILE=/run/secrets/mariadb_db
      - MYSQL_USER_FILE=/run/secrets/mariadb_user
    secrets:
      - mariadb_root_password
      - mariadb_password
      - mariadb_user
      - mariadb_db

  nextcloud:
    image: nextcloud:production-fpm
    container_name: nextcloud-subdir
    links:
      - mariadb
    volumes:
      - nextcloud:/var/www/html
    environment:
      - MYSQL_PASSWORD_FILE=/run/secrets/mariadb_password
      - MYSQL_DATABASE_FILE=/run/secrets/mariadb_db
      - MYSQL_USER_FILE=/run/secrets/mariadb_user
      - MYSQL_HOST=mariadb
      - NEXTCLOUD_ADMIN_PASSWORD_FILE=/run/secrets/nextcloud_admin_password
      - NEXTCLOUD_ADMIN_USER_FILE=/run/secrets/nextcloud_admin_user
      - NEXTCLOUD_TRUSTED_DOMAINS=localhost
      # (un)comment to switch nextcloud being served in "/nextcloud" (with adapted proxy configuration)
      - OVERWRITEWEBROOT=/nextcloud
    secrets:
      - mariadb_password
      - mariadb_user
      - mariadb_db
      - nextcloud_admin_password
      - nextcloud_admin_user

  web:
    image: nginx
    container_name: nginx-subdir
    ports:
      - 80:80
    links:
      - nextcloud
    volumes:
      # (un)comment to switch nextcloud being served in "/nextcloud" (with adapted OVERWRITEWEBROOT variable)
      #- ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx_subdir.conf:/etc/nginx/nginx.conf:ro
    volumes_from:
      - nextcloud
      
secrets:
    # files are supposed to contain only usernames or passwords and a newline
    nextcloud_admin_password:
      file: ./nextcloud_admin_password.txt
    nextcloud_admin_user:
      file: ./nextcloud_admin_user.txt
    mariadb_root_password:
      file: ./mariadb_root_password.txt
    mariadb_db:
      file: ./mariadb_db.txt
    mariadb_user:
      file: ./mariadb_user.txt
    mariadb_password:
      file: ./mariadb_passwort.txt

The nginx.conf is https://raw.githubusercontent.com/nextcloud/docker/master/.examples/docker-compose/insecure/mariadb/fpm/web/nginx.conf the user.txt and password.txt files are simple textfiles containing the values for initialization.

The nginx_subdir.conf:

worker_processes auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

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

    sendfile        on;
    #tcp_nopush     on;

    # Prevent nginx HTTP Server Detection
    server_tokens   off;

    keepalive_timeout  65;

    #gzip  on;

    upstream php-handler {
        server nextcloud:9000;
    }

    server {
        listen 80;

        # 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/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;

        # 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 /var/www/html;

        # 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 = /nextcloud/ {
            if ( $http_user_agent ~ ^DavClnt ) {
                add_header debuglocation DavClnt always;
                return 302 /nextcloud/remote.php/webdav/$is_args$args;
            }
        }

        location = /nextcloud/robots.txt {
            add_header debuglocation robots always;
            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 ^~ nextcloud/.well-known {
            add_header debuglocation well-known always;

            # The rules in this block are an adaptation of the rules
            # in `.htaccess` that concern `/.well-known`.
            rewrite ^/localhost/nextcloud(.*) $1 break;

            location = nextcloud/.well-known/carddav { return 301 nextcloud/remote.php/dav/; }
            location = nextcloud/.well-known/caldav  { return 301 nextcloud/remote.php/dav/; }

            location nextcloud/.well-known/acme-challenge    { try_files $uri $uri/ =404; }
            location nextcloud/.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 nextcloud/index.php$request_uri;
        }

        # Rules borrowed from `.htaccess` to hide certain paths from clients
        location ~ ^/nextcloud/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  {
            add_header debuglocation internal_1 always;
            return 404;
        }
        location ~ ^/nextcloud/(?:\.|autotest|occ|issue|indie|db_|console)                {
            add_header debuglocation internal_2 always;
            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(?:$|/) {
            add_header debuglocation PHP always;

            rewrite ^/localhost/nextcloud(.*) $1;
            # 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;
            add_header debuglocation2 $path_info always;

            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)$ {
            add_header debuglocation images always;
            rewrite ^/localhost/nextcloud(.*) $1 break;
            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?$ {
            add_header debuglocation woff2 always;
            rewrite ^/localhost/nextcloud(.*) $1 break;
            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 /nextcloud/remote {
            add_header debuglocation remote always;
            return 301 /nextcloud/remote.php$request_uri;
        }

        location /nextcloud/ {
            add_header debuglocation nextcloud always;
            rewrite ^/localhost/nextcloud(.*) $1 break;
            try_files $uri $uri/ /index.php$request_uri;
        }
    }
}

In this I tried to add the rewrite rule rewrite ^/localhost/nextcloud(.*) $1 break; to all relevant locations and added add_header debuglocation to all locations for debugging them with curl -v (thanks to https://stackoverflow.com/questions/12703702/nginx-test-which-location-used-to-process-request). Calling curl -v http://localhost/nextcloud/ > /dev/null returns a 404 but without debuglocation populated.

Marcel avatar
gb flag
If you remove the /nextcloud from all locations and all the rewrite rules, does nginx correctly serves the static files?
Marcel avatar
gb flag
I'm asking because I don't think you understand all the concepts involved. Serving an app from a folder path logically isn't just a matter of adding the path and the rewrite rules to all locations, you also have to set the host header. I think you need to add a catch-all location with a proxy_pass to the upstream.
soriak avatar
cn flag
@Marcel thanks for your reply. Yes, running nextcloud and nginx with the default configuration (linked) without path and the above compose.yml nextcloud is served as expected. I'm quite new to nginx and reverse proxies in particular. I also thought if an other server configuration in ngxinx handling the rewrite would be the best way, but wasn't successful so far. Would this be a way one would solve it? I will look into setting host headers.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.