What could be causing too many redirects?

I am trying to run nextcloud with caddy. Here is my config based on what I have seen here

root /var/www/localhost/htdocs/nextcloud
errors /var/log/caddy-errors.log
fastcgi / 127.0.0.1:9000 php
rewrite {
    r ^/remote.php/(webdav|caldav|carddav|dav)(\/?)$
    to /remote.php/{1}
  }

rewrite {
    r ^/remote.php/(webdav|caldav|carddav|dav)/(.+)(\/?)$
    to /remote.php/{1}/{2}
  }

I get to the login page and I can enter the login. Right after that, nextcloud apparently attempts to go to index.php/apps/files and gets into an infinite loop of redirects.

I don’t see this problem if I run with apache and mod_php.

Well, you haven’t given Caddy any redirection instructions and none of those rewrites apply to that path. Those requests would be going to fastcgi… I think. Does your PHP log record anything of note during these redirect loops?

Other than

the log contains nothing else.

Unfortunate, I was hoping it would be a bit more enlightening.

Try add an access log (log /var/log/caddy-access.log or similar) and see if we can rule out whatever Caddy’s responding with.

What’s the equivalent apache config look like? And what’s the Nextcloud version?

The access log looks like this

192.168.1.152 - [22/Mar/2017:00:51:23 -0400] "GET /index.php HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:51:23 -0400] "GET /index.php/login HTTP/1.1" 200 17043
192.168.1.152 - [22/Mar/2017:00:51:23 -0400] "GET /index.php/core/js/oc.js?v=afc7ab9bc4e13e38dbea66e164c982e8 HTTP/1.1" 200 2935
192.168.1.152 - [22/Mar/2017:00:51:23 -0400] "GET /cron.php HTTP/1.1" 200 20
192.168.1.152 - [22/Mar/2017:00:52:06 -0400] "POST /index.php/login HTTP/1.1" 303 0
192.168.1.152 - [22/Mar/2017:00:52:06 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:06 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:07 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:07 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:07 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:07 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:07 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:07 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:07 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:07 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:08 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:08 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:08 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:08 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:08 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0
192.168.1.152 - [22/Mar/2017:00:52:08 -0400] "GET /index.php/apps/files/ HTTP/1.1" 302 0

The whole of the apache config is scattered across files, but the relevant section is

DocumentRoot "/var/www/localhost/htdocs/nextcloud"
<FilesMatch \.php$>
                SetHandler application/x-httpd-php
</FilesMatch>
<Directory "/var/www/localhost/htdocs/nextcloud">
	Options Indexes FollowSymLinks
	AllowOverride All
	Require all granted
</Directory>

And the .htaccess?

There is no .htaccess

Actually, there is one in the root of nextcloud

<IfModule mod_headers.c>
  <IfModule mod_setenvif.c>
    <IfModule mod_fcgid.c>
       SetEnvIfNoCase ^Authorization$ "(.+)" XAUTHORIZATION=$1
       RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION
    </IfModule>
    <IfModule mod_proxy_fcgi.c>
       SetEnvIfNoCase Authorization "(.+)" HTTP_AUTHORIZATION=$1
    </IfModule>
  </IfModule>

  <IfModule mod_env.c>
    # Add security and privacy related headers
    Header set X-Content-Type-Options "nosniff"
    Header set X-XSS-Protection "1; mode=block"
    Header set X-Robots-Tag "none"
    Header set X-Frame-Options "SAMEORIGIN"
    Header set X-Download-Options "noopen"
    Header set X-Permitted-Cross-Domain-Policies "none"
    SetEnv modHeadersAvailable true
  </IfModule>

  # Add cache control for static resources
  <FilesMatch "\.(css|js|svg|gif)$">
    Header set Cache-Control "max-age=15778463"
  </FilesMatch>
  
  # Let browsers cache WOFF files for a week
  <FilesMatch "\.woff$">
    Header set Cache-Control "max-age=604800"
  </FilesMatch>
</IfModule>
<IfModule mod_php5.c>
  php_value upload_max_filesize 511M
  php_value post_max_size 511M
  php_value memory_limit 512M
  php_value mbstring.func_overload 0
  php_value always_populate_raw_post_data -1
  php_value default_charset 'UTF-8'
  php_value output_buffering 0
  <IfModule mod_env.c>
    SetEnv htaccessWorking true
  </IfModule>
</IfModule>
<IfModule mod_php7.c>
  php_value upload_max_filesize 511M
  php_value post_max_size 511M
  php_value memory_limit 512M
  php_value mbstring.func_overload 0
  php_value default_charset 'UTF-8'
  php_value output_buffering 0
  <IfModule mod_env.c>
    SetEnv htaccessWorking true
  </IfModule>
</IfModule>
<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
  RewriteRule ^\.well-known/host-meta /public.php?service=host-meta [QSA,L]
  RewriteRule ^\.well-known/host-meta\.json /public.php?service=host-meta-json [QSA,L]
  RewriteRule ^\.well-known/carddav /remote.php/dav/ [R=301,L]
  RewriteRule ^\.well-known/caldav /remote.php/dav/ [R=301,L]
  RewriteRule ^remote/(.*) remote.php [QSA,L]
  RewriteRule ^(?:build|tests|config|lib|3rdparty|templates)/.* - [R=404,L]
  RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/.*
  RewriteRule ^(?:\.|autotest|occ|issue|indie|db_|console).* - [R=404,L]
</IfModule>
<IfModule mod_mime.c>
  AddType image/svg+xml svg svgz
  AddEncoding gzip svgz
</IfModule>
<IfModule mod_dir.c>
  DirectoryIndex index.php index.html
</IfModule>
AddDefaultCharset utf-8
Options -Indexes
<IfModule pagespeed_module>
  ModPagespeed Off
</IfModule>

Hrm… Glancing over that .htaccess, you could probably add a header or two and some expires to your Caddyfile, but I don’t see any equivalent to those rewrites. Where do they come from?

Where do which rewrites come from?

rewrite {
    r ^/remote.php/(webdav|caldav|carddav|dav)(\/?)$
    to /remote.php/{1}
  }

rewrite {
    r ^/remote.php/(webdav|caldav|carddav|dav)/(.+)(\/?)$
    to /remote.php/{1}/{2}
  }

I got them from a configuration that was posted here.

The closest equivalent I can see in the .htaccess is this:

  RewriteRule ^\.well-known/carddav /remote.php/dav/ [R=301,L]
  RewriteRule ^\.well-known/caldav /remote.php/dav/ [R=301,L]
  RewriteRule ^remote/(.*) remote.php [QSA,L]

But they aren’t the same. The other rewrites look like LetsEncrypt support, actually (not required because Caddy will handle it all). Pull those rewrites out of your Caddyfile (unless you know what they’re for and that they’re required). I doubt it will solve your redirect issue though (those rewrites shouldn’t be affecting that request path).

Removed. It had no effect.

At this point I suspect Nextcloud itself is the culprit, possibly taking some issue with the headers or the request.

You might as well add the security headers from the .htaccess:

  <IfModule mod_env.c>
    # Add security and privacy related headers
    Header set X-Content-Type-Options "nosniff"
    Header set X-XSS-Protection "1; mode=block"
    Header set X-Robots-Tag "none"
    Header set X-Frame-Options "SAMEORIGIN"
    Header set X-Download-Options "noopen"
    Header set X-Permitted-Cross-Domain-Policies "none"
    SetEnv modHeadersAvailable true
  </IfModule>

It would look something like this:

header / {
    X-Content-Type-Options "nosniff"
    X-XSS-Protection "1; mode=block"
    X-Robots-Tag "none"
    X-Frame-Options "SAMEORIGIN"
    X-Download-Options "noopen"
    X-Permitted-Cross-Domain-Policies "none"
}

This rewrite rule I am unfamiliar with and can’t say for sure whether it is contributory to your issue, but it seems suspect and I’ve no idea how to replicate its effect in a Caddyfile:

RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

This too:

    <IfModule mod_fcgid.c>
       SetEnvIfNoCase ^Authorization$ "(.+)" XAUTHORIZATION=$1
       RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION
    </IfModule>
    <IfModule mod_proxy_fcgi.c>
       SetEnvIfNoCase Authorization "(.+)" HTTP_AUTHORIZATION=$1
    </IfModule>

Seems to be important. I think Authorization is a header that’s being expected. It might be required to be set in the FastCGI environment.

Maybe try modifying your fastcgi directive:

fastcgi / 127.0.0.1:9000 php {
    env "HTTP_AUTHORIZATION" {>Authorization}
}

My Apache-fu is a little weak, so if anyone with a better understanding could take a look, they might be able to advise.

Made no difference.

Make sure your cache is cleared each time too. 301 redirects are cached, so if you’re making changes to config but not clearing cache, all your changes need to be redone with a clear cache.

2 Likes

Yeah, Google Chrome definitely caches 301. It’s a real pain if you aren’t aware that it’s happening. You can disable caching while DevTools are open. see: Chrome Clear Redirect Cache

1 Like

I initially had redirects in nginx, but solved it with using their recommended configuration. This implies the caddy configuration is causing the redirects after all.

server {
        listen 80;
        server_name cloud.example.com;
        root /var/www/localhost/htdocs/nextcloud;

    location / {
        rewrite ^ /index.php$uri;
    }

    location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+|core/templates/40[34])\.php(?:$|/) {
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_pass 127.0.0.1:9000;
    }

}

What needs to be changed in the caddy configuration to match this?