Nextcloud returning HTTP 403 errors when syncing files using Linux Desktop sync app

Nextcloud version: 20.0.5
Operating system and version: Debian Buster
Nginx version: 1.19.6
PHP version: 7.4
PostgreSQL version: 13.1

The issue you are facing:
Yesterday I set up a new Nextcloud instance using Nginx as the web server and Postgres as the database. I wanted to migrate away from an existing instance and figured setting this up would be a simple enough task to do myself. While the installation went OK, I’m having an issue syncing local files on my desktop to my Nextcloud instance using the official Nextcloud Desktop Sync application.

I noticed that when the Sync app tries to upload files greater than 10MB or so in size, I get this error message returned from the server:

[error] 4607#4607: *938 access forbidden by rule, client: $MY_IP_ADDRESS, server: $MY_DOMAIN, request: "MOVE /remote.php/dav/uploads/$MY_USER_ID/2726568773/.file HTTP/1.1", host: "$MY_DOMAIN"

I don’t know why this is happening, as this never occurred when syncing files to my previous Nextcloud instance.

Below are my config files:

The output of your config.php file in /path/to/nextcloud (make sure you remove any identifiable information!):

$CONFIG = array (
  'instanceid' => '$INSTANCE_ID',
  'passwordsalt' => '$SALT',
  'secret' => '$SECRET',
  'trusted_domains' => 
  array (
    0 => '$MY_DOMAIN',
  'datadirectory' => '/var/www/nextcloud/data',
  'dbtype' => 'pgsql',
  'version' => '',
  'overwrite.cli.url' => '$MY_DOMAIN',
  'dbname' => '$DB_NAME',
  'dbhost' => '$DB_HOST',
  'dbport' => '',
  'dbtableprefix' => 'oc_',
  'dbuser' => '$DB_USER',
  'dbpassword' => '',
  'installed' => true,
  'memcache.local' => '\\OC\\Memcache\\APCu',
  'twofactor_enforced' => 'true',
  'twofactor_enforced_groups' => 
  array (
  'twofactor_enforced_excluded_groups' => 
  array (
  'updater.secret' => '$UPDATE_SECRET',
  'maintenance' => false,
  'theme' => '',
  'loglevel' => 2,

The output of your Nginx config in /etc/nginx/nginx.conf:

user                 www-data;
pid                  /var/run/;
worker_processes     auto;
worker_rlimit_nofile 65535;

events {
    multi_accept       on;
    worker_connections 65535;

http {
    charset              utf-8;
    sendfile             on;
    tcp_nopush           on;
    tcp_nodelay          on;
    server_tokens        off;
    log_not_found        off;
    types_hash_max_size  2048;
    client_max_body_size 100M;

    # MIME
    include              mime.types;
    default_type         application/octet-stream;

    # Logging
    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;
    error_log            /var/log/nginx/error.log warn;

    # SSL
    ssl_session_timeout  1d;
    ssl_session_cache    shared:SSL:10m;
    ssl_session_tickets  off;

    # Mozilla Modern configuration
    ssl_protocols        TLSv1.2 TLSv1.3;

    # OCSP Stapling
    ssl_stapling         on;
    ssl_stapling_verify  on;
    resolver    [2606:4700:4700::1111] [2606:4700:4700::1001] [2001:4860:4860::8888] [2001:4860:4860::8844] valid=60s;
    resolver_timeout     2s;

    # Load configs
    include              /etc/nginx/conf.d/*.conf;
    include              /etc/nginx/sites-enabled/*;

The output of your Nginx site config at /etc/nginx/sites-enabled/nextcloud.conf:

upstream php-handler {
	server unix:/var/run/php/php7.4-fpm.sock;

# subdomains redirect
server {
	listen	443 ssl http2;
	listen	[::]:443 ssl http2;
	server_name	*.$MY_DOMAIN;

	# SSL
	ssl_certificate	/etc/letsencrypt/live/$MY_DOMAIN/fullchain.pem;
	ssl_certificate_key	/etc/letsencrypt/live/$MY_DOMAIN/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/$MY_DOMAIN/chain.pem;

	return	301 https://$MY_DOMAIN$request_uri;

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	server_name $MY_DOMAIN;
	set $base /var/www/nextcloud;
	root $base/;

	# SSL
	ssl_certificate	/etc/letsencrypt/live/$MY_DOMAIN/fullchain.pem;
	ssl_certificate_key	/etc/letsencrypt/live/$MY_DOMAIN/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/$MY_DOMAIN/chain.pem;

	ssl_protocols TLSv1.3 TLSv1.2;
	ssl_prefer_server_ciphers on;
	ssl_dhparam dhparams.pem;

	# security
	include include/security.conf;

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

	client_max_body_size 512M;
	fastcgi_buffers 64 4K;

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

	location ^~ /.well-known {
		rewrite ^/\.well-known/host-meta\.json  /public.php?service=host-meta-json  last;
	        rewrite ^/\.well-known/host-meta        /public.php?service=host-meta       last;
	        rewrite ^/\.well-known/webfinger        /public.php?service=webfinger       last;
	        rewrite ^/\.well-known/nodeinfo         /public.php?service=nodeinfo        last;

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

	        try_files $uri $uri/ =404;

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

	location ~ \.php(?:$|/) {
		create_full_put_path on;
		dav_access user:rw group:rw all:r;

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

		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;

		try_files $fastcgi_script_name =404;

	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

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

	# general configs
	include include/general.conf;

I also have a separate config for letsencrypt found at /etc/nginx/sites-available/letsencrypt.conf:

server {
	listen		80 default_server;
	listen		[::]:80 default_server;
	server_name	$MY_DOMAIN;
	root		/var/www/_letsencrypt;

	location ^~ /\.(?!well-known)/ {
		return 301 https://$server_name$request_uri;

Have the same issue also uploading files larger than 10Mb directly !
Seems the problem is related to this OLD bug of the sabre module used for dav file uploads

Looking at a solution/manual fix now …

1 Like

I am experiencing the same issue. Were you able to solve it?

1 Like

Same here, any insights?