Nginx Reverse Proxy Config.php Setup

The Basics

  • Nextcloud Server version (e.g., 29.x.x):
    • 31.0.4.1
  • Operating system and version (e.g., Ubuntu 24.04):
    • Ubuntu 24.04.2 LTS
  • Web server and version (e.g, Apache 2.4.25):
    • nginx 1.29.0
  • Reverse proxy and version _(e.g. nginx 1.27.2)
    • nginx 1.29.0
  • PHP version (e.g, 8.3):
    • 8.3.22
  • Is this the first time you’ve seen this error? (Yes / No):
    • Yes
  • When did this problem seem to first start?
    • Upon reverse proxy setup attempt
  • Installation method (e.g. AlO, NCP, Bare Metal/Archive, etc.)
    • Bare Metal
  • Are you using CloudfIare, mod_security, or similar? (Yes / No)
    • No

Summary of the issue you are facing:

I have a web server and Nextcloud hosted locally on a server. I successfully have been able to access them directly from the browser. However, I attempted to create a reverse proxy for both and have a config issue. As of now the request times out.

The web server, being far simpler, is functional with the reverse proxy, but I have yet to setup Nextcloud. Any sanity check and explanation regarding config parameters would be greatly appreciated!

I also have an active thread in nginx community support, and as of now it seems as though the nginx config is valid.

I am currently away from the server, but will return in the evening (PST) on 07 July.

Log entries

Web server / Reverse Proxy

The output of your Apache/nginx/system log in /var/log/____:

  1. Access log
# Successful web server requests
192.168.1.145 - - [06/Jul/2025:16:02:33 +0000] "GET /charlie/ HTTP/2.0" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0" "-"
192.168.1.145 - - [06/Jul/2025:16:02:33 +0000] "GET /charlie/charlie.js HTTP/2.0" 404 153 "https://optiplex/charlie/" "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0" "-"

# Timed out Nextcloud requests
127.0.0.1 - - [06/Jul/2025:16:03:44 +0000] "GET /nextcloud/ HTTP/1.0" 302 20 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0" "192.168.1.145"
192.168.1.145 - - [06/Jul/2025:16:03:44 +0000] "GET /nextcloud/ HTTP/2.0" 302 20 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0" "-"
  1. Error log
# Nextcloud 
2025/07/06 16:03:44 [notice] 18618#18618: *8 "^/nextcloud/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy)" does not match "/nextcloud/index.php", client: 127.0.0.1, server: , request: "GET /nextcloud/ HTTP/1.0", host: "optiplex"

Configuration

Nextcloud

The output of occ config:list system or similar is best, but, if not possible, the contents of your config.php file from /path/to/nextcloud is fine (make sure to remove any identifiable information!):

<?php
$CONFIG = array (
  'passwordsalt' => '',
  'secret' => '',
  #---------    
  # Access |
  #---------
  'trusted_domains' =>
  array (
    0 => 'localhost:82',
  ),
  'allowed_admin_ranges' =>
  array (
    0 => '127.0.0.1/8',
    1 => '192.168.1.145/24',
  ),
  'twofactor_enforced' => 'true',
  'twofactor_enforced_groups' =>
  array (
  ),
  'twofactor_enforced_excluded_groups' =>
  array (
  ),
  #----------------
  # Reverse proxy | https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/reverse_proxy_configuration.html#example
  #----------------
  'trusted_proxies' => ['localhost'],                      
  'overwritehost' => 'optiplex:82',                   # Proxy hostname + port
  'overwriteprotocol' => 'https',                       # Proxy protocol (HTTP/HTTPS)
  'overwritewebroot' => '',                             # ?
  'overwritecondaddr' => '',                            # ?
  'overwrite.cli.url' => 'http://localhost:82',            # ?
  #-------
  # Misc |
  #-------
  'version' => '31.0.4.1',
  'default_phone_region' => 'US',
  'maintenance_window_start' => 1,
  #-----------
  # Database |
  #-----------
  'datadirectory' => '/var/www/nextcloud/data',
  'dbtype' => 'mysql',
  'dbname' => 'nc_db',
  'dbhost' => 'localhost',
  'dbport' => '',
  'dbtableprefix' => 'oc_',
  'mysql.utf8mb4' => true,
  'dbuser' => 'nc_user',
  'dbpassword' => '',
  'installed' => true,
  'instanceid' => '',
  #--------
  # Redis | https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/caching_configuration.html#
  #--------
  'redis' =>
  array (
    'host' => '/run/redis/redis-server.sock',
    'port' => 0,
    'timeout' => 0.0,
    'password' => '',
  ),
  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'memcache.local' => '\\OC\\Memcache\\APCu',
  'maintenance' => false,
  #-------
  # Mail |
  #-------
  'mail_smtpmode' => 'smtp',
  'mail_smtpauth' => true,
  'mail_sendmailmode' => 'smtp',
  'mail_smtpname' => '',
  'mail_smtppassword' => '',
);

nginx

# Documentation |
#----------------
# Directive directory	https://nginx.org/en/docs/dirindex.html
# Reverse proxy		https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/
# Server		https://docs.nginx.com/nginx/admin-guide/web-server/web-server/

#-------
# Main |
#-------

# Defines worker processes' user + group credentials
user www-data;

# Quantity = CPU cores 
worker_processes 2;

# Location + logging level
error_log /var/log/nginx/error_main.log debug;

# Defines file containing main PID
pid /run/nginx.pid;

events {
	# Max simultaneous connections per worker process
	worker_connections  	1024;
}

http {
	#-------
	# Logs |
	#-------

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

	# Jellyfin: Censor sensitive logs
	log_format stripsecrets '$remote_addr $host - $remote_user [$time_local] '
                    '"$secretfilter" $status $body_bytes_sent '
                    '$request_length $request_time $upstream_response_time '
                    '"$http_referer" "$http_user_agent"';

	# Jellyfin
	map $request $secretfilter {
    		~*^(?<prefix1>.*[\?&]api_key=)([^&]*)(?<suffix1>.*)$  "${prefix1}***$suffix1";
    		default                                               $request;
	}

	error_log 	/var/log/nginx/error_http.log debug;

	# main applies pre-defined log_format
	access_log	/var/log/nginx/access_http.log main;

	#-------
	# MIME |
	#-------

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

	#-----------------------------
	# File transfer optimization |
	#-----------------------------

	sendfile        	on;

	# Provide client file w/out buffering. Improves static content transfer rate. Utilize w/ static content servers.
	tcp_nopush     		off;

	# Limits data transer amount per sendfile() call. Prevents individual call completely seizing worker processes.
	sendfile_max_chunk	2m;

	#------------------------------
	# TCP connection optimization |
	#------------------------------

	# Low traffic site. Low value minimizes idle connections.
	keepalive_timeout	30;

	# Max requests per keepalive connection
	keepalive_requests	100;

	#------------
	# Nextcloud |
	#------------

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

	# Set the `immutable` cache control options only for assets with a cache busting `v` argument
	map $arg_v $asset_immutable {
    		"" "";
    		default ", immutable";
	}

	#----------------
	# Reverse proxy | Redifining request headers unnecessary. Proxied services located locally.
	#----------------
	#include /etc/nginx/sites-enabled/reverse_proxy.conf;

	server {
		listen  443 ssl;
		http2   on;

		#----------
		# SSL/TLS | https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/
		#----------

		ssl_certificate		/etc/ssl/certs/oller-net-selfsigned.crt;
		ssl_certificate_key	/etc/ssl/private/oller-net-selfsigned.key;
		ssl_protocols		TLSv1.3;

		# Reuse in Nextcloud config
		#add_header 		Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

		#-----------------------
		# SSL/TLS optimization | https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/#optimize
		#-----------------------

		# 1m = 4k sessions
        	ssl_session_cache       shared:SSL-TLS:1m;
        	ssl_session_timeout     10m;

		location /charlie {
			allow   192.168.1.0/24;
	        	deny    all;

			proxy_pass http://localhost:81;
		}

		#-----------
		# Jellyfin |
		#-----------

		# Censor sensitive logs
		access_log /var/log/nginx/access.log stripsecrets;

		## The default `client_max_body_size` is 1M, this might not be enough for some posters, etc.
    		client_max_body_size 20M;

    		# Security / XSS Mitigation Headers
    		add_header X-Content-Type-Options "nosniff";

    		# Permissions policy. May cause issues with some clients
    		add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), camera=(), clipboard-read=(), display-capture=(), document-domain=(), encrypted-media=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), keyboard-map=(), local-fonts=(), magnetometer=(), microphone=(), payment=(), publickey-credentials-get=(), serial=(), sync-xhr=(), usb=(), xr-spatial-tracking=()" always;

    		# Content Security Policy
    		# See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
    		# Enforces https content and restricts JS/CSS to origin
    		# External Javascript (such as cast_sender.js for Chromecast) must be whitelisted.
    		add_header Content-Security-Policy "default-src https: data: blob: ; img-src 'self' https://* ; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com https://www.youtube.com blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'; font-src 'self'";

    		location /jellyfin {
			allow 	192.168.1.0/24;
			deny	all;

        		# Proxy main Jellyfin traffic
        		proxy_pass http://localhost:8096;
        		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_set_header X-Forwarded-Protocol $scheme;
        		proxy_set_header X-Forwarded-Host $http_host;

        		# Disable buffering when the nginx proxy gets very resource heavy upon streaming
        		proxy_buffering off;
    		}

    		location /socket {
			allow 	192.168.1.0/24;
			deny	all;

        		# Proxy Jellyfin Websockets traffic
        		proxy_pass http://localhost:8096;
        		proxy_http_version 1.1;
        		proxy_set_header Upgrade $http_upgrade;
        		proxy_set_header Connection "upgrade";
        		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_set_header X-Forwarded-Protocol $scheme;
        		proxy_set_header X-Forwarded-Host $http_host;
    		}

		#------------
		# Nextcloud |
		#------------

		location /nextcloud {
			allow 	192.168.1.0/24;
			deny 	all;

			proxy_pass http://localhost:8080;

			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;

			add_header		    Front-End-Https on;
		}
		location /.well-known/carddav {
			allow 	192.168.1.0/24;
			deny 	all;

			return 301 $scheme://$host/remote.php/dav;
		}
		location /.well-known/caldav {
			allow 	192.168.1.0/24;
			deny 	all;

			return 301 $scheme://$host/remote.php/dav;
		}
		location ^~ /.well-known {
    			allow 	192.168.1.0/24;
			deny 	all;

			return 301 $scheme://$host/index.php$uri;
		}
	}

	#-------------------
	# Proxied services |
	#-------------------

	server {
		listen	127.0.0.1:81;
		http2 	on;

		include /etc/nginx/sites-available/charlie.conf;
	}
	server {
		listen	127.0.0.1:8080;
		http2 	on;

		include /etc/nginx/sites-enabled/nextcloud.conf;
	}
}

Apps

The output of occ app:list (if possible).

Enabled:
  - activity: 4.0.0
  - admin_audit: 1.21.0
  - app_api: 5.0.2
  - bruteforcesettings: 4.0.0
  - calendar: 5.3.4
  - circles: 31.0.0
  - cloud_federation_api: 1.14.0
  - comments: 1.21.0
  - contactsinteraction: 1.12.0
  - dashboard: 7.11.0
  - dav: 1.33.0
  - deck: 1.15.1
  - federatedfilesharing: 1.21.0
  - federation: 1.21.0
  - files: 2.3.1
  - files_downloadlimit: 4.0.0
  - files_pdfviewer: 4.0.0
  - files_reminders: 1.4.0
  - files_sharing: 1.23.1
  - files_trashbin: 1.21.0
  - files_versions: 1.24.0
  - firstrunwizard: 4.0.0
  - logreader: 4.0.0
  - lookup_server_connector: 1.19.0
  - nextcloud_announcements: 3.0.0
  - notes: 4.12.1
  - notifications: 4.0.0
  - oauth2: 1.19.1
  - password_policy: 3.0.0
  - photos: 4.0.0-dev.1
  - privacy: 3.0.0
  - profile: 1.0.0
  - provisioning_api: 1.21.0
  - recommendations: 4.0.0
  - related_resources: 2.0.0
  - richdocuments: 8.7.1
  - richdocumentscode: 25.4.202
  - serverinfo: 3.0.0
  - settings: 1.14.0
  - sharebymail: 1.21.0
  - support: 3.0.0
  - survey_client: 3.0.0
  - suspicious_login: 9.0.1
  - systemtags: 1.21.1
  - tasks: 0.16.1
  - text: 5.0.0
  - theming: 2.6.1
  - twofactor_backupcodes: 1.20.0
  - twofactor_totp: 13.0.0-dev.0
  - updatenotification: 1.21.0
  - user_status: 1.11.0
  - viewer: 4.0.0
  - weather_status: 1.11.0
  - webhook_listeners: 1.2.0
  - whiteboard: 1.1.2
  - workflowengine: 2.13.0
Disabled:
  - encryption: 2.19.0
  - files_external: 1.23.0
  - twofactor_nextcloud_notification: 5.0.0
  - user_ldap: 1.22.0

So if I’m reading this right it appears you’re using a single Nginx installation to serve as a reverse proxy for services it is also itself hosting as a web server.

Strictly speaking you could probably just forget the RP stuff, and just treat your Nc as an Nginx based subdirectory installation (using the configuration for a subfolder install that is provided in the Nextcloud *Admin Manual"). However it should also work either way.

What does nextcloud.conf contain?

Sorry for the late reply!

  1. Yes. I am using a single nginx instance to host services while simultaneously acting as its own reverse proxy.
  2. I’d prefer to keep the RP as I am learning much by attempting to configure it and I also hope to one day make my services accessible via the public internet.
  3. Nextcloud.conf: The only config I am aware of for NC is /var/www/nextcloud/config/config.php?

I was referring to this config file that you’re including.

Nextcloud.conf

	# Prevent nginx HTTP Server Detection
        server_tokens off;

        # Path to the root of the domain
        root /var/www;

        # Use Mozilla's guidelines for SSL/TLS settings
        # https://mozilla.github.io/server-side-tls/ssl-config-generator/

        # NOTE: Line 40 duplicate?
        # Prevent nginx HTTP Server Detection
        # server_tokens off;

        # Set .mjs and .wasm MIME types
        # Either include it in the default mime.types list
        # and include that list explicitly or add the file extension
        # only for Nextcloud like below:
        include mime.types;
        types {
                text/javascript mjs;
                application/wasm wasm;
         }

        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 the Nextcloud `.htaccess` that concern `/.well-known`.

                # ENSURE PROPER CONFIG VIA SOURCE LINK 
                location = /.well-known/carddav { return 301 /nextcloud/remote.php/dav/; }
                location = /.well-known/caldav  { return 301 /nextcloud/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 /nextcloud/index.php$request_uri;
        }

        location ^~ /nextcloud {
                # set max upload size and increase upload timeout:
                client_max_body_size 512M;
                client_body_timeout 300s;
                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 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;

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

                # The settings allows you to optimize the HTTP2 bandwidth.
                # See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/
                # for tuning hints
                client_body_buffer_size 512k;

		# ENABLING PREVENTS REVERSE PROXY
                # 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=31536000; includeSubDomains; preload" always;

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

                # Remove X-Powered-By, which is an information leak
                fastcgi_hide_header X-Powered-By;

                # Specify how to handle directories -- specifying `/nextcloud/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`, `/ocs-provider`), and thus
                # `try_files $uri $uri/ /nextcloud/index.php$request_uri`
                # always provides the desired behaviour.
                index index.php index.html /nextcloud/index.php$request_uri;

                # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
                location = /nextcloud {
                        if ( $http_user_agent ~ ^DavClnt ) {
                                return 302 /nextcloud/remote.php/webdav/$is_args$args;
                        }
                }

                # Rules borrowed from `.htaccess` to hide certain paths from clients
                location ~ ^/nextcloud/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)    { return 404; }
                location ~ ^/nextcloud/(?:\.|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
# `/nextcloud/index.php` to the URI, resulting in a HTTP 500 error response.
                location ~ \.php(?:$|/) {
                        # Required for legacy support
                        rewrite ^/nextcloud/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /nextcloud/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;
                }

                # Serve static files
                location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac)$ {
                        try_files $uri /nextcloud/index.php$request_uri;
                        # HTTP response headers borrowed from Nextcloud `.htaccess`
                        add_header Cache-Control                     "public, max-age=15778463$asset_immutable";
                        add_header Referrer-Policy                   "no-referrer"       always;
                        add_header X-Content-Type-Options            "nosniff"           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;
                        access_log off;     # Optional: Don't log access to assets
  		}

                location ~ \.(otf|woff2?)$ {
                        try_files $uri /nextcloud/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 {
                                return 301 /nextcloud/remote.php$request_uri;
                        }

                location /nextcloud {
                        try_files $uri $uri/ /nextcloud/index.php$request_uri;
                }
        }

        # Suppress log messages (nginx section 3)
        location = /data/htaccesstest.txt {
                allow all;
                log_not_found off;
                access_log off;
        }

@jtr Nextcloud.conf is based on the example provided in Nextcloud documentation.

@jtr Is there any additional information you need? Also, no pressure, I understand you may be busy.