Nginx Nextcloud too many redirects
You have a single server
definition for both HTTP
and HTTPS
and, as you noticed, the lines:
if ($host = nextcloud.domain.com) {
return 301 https://$server_name:443$request_uri;
} # managed by Certbot
are doing most of the damage. Since it is a 301
redirect, you browser will remember it until it is restarted.
Split the configuration of the two listening ports, so that the first lines are:
server {
listen 80;
server_name nextcloud.domain.com;
return 301 https://$server_name$request_uri; # managed by Certbot
}
server {
listen 443 ssl http2;
server_name nextcloud.domain.com;
# rest of the config
Edit: As you remarked, you can put both listen
directives in one server
block, however this will cause a redirect loop if your server is accessed directly using the nextcloud.domain.com
. Cloudflare probably puts another proxy server between your clients and nginx and forwards requests using your IP instead of the domain name. So the loop remains hidden, but is is still there.
If you want to keep the single server block, you should consider changing the if
condition to:
if ($scheme = http) {
return 301 https://$server_name$request_uri;
} # managed by Certbot
However, in terms of clarity and performance, it is probably worse than two server
blocks.
Related videos on Youtube
Georgi Stoyanov
Updated on September 18, 2022Comments
-
Georgi Stoyanov over 1 year
I am trying to install Nextcloud on my Raspberry Pi.
- I have created an
A record
in Cloudflare with subdomain pointing to my Raspberry Pi IP address and configured theNGINX
withcertbot
using the default configuration file in/etc/nginx/sites-available/nextcloud
.- I have created symlink in
/etc/nginx/sites-enables
and checked theNGINX
configuration withsudo nginx -t
and the configuration was all valid. - I am getting successfully redirected to
https
but I am getting an error that I am getting too many redirects when I try to open thenextcloud.mydomain.com
in a browser.
- I have created symlink in
This is my
nextcloud
config:upstream php-handler { server 127.0.0.1:9000; #server unix:/var/run/php/php7.3-fpm.sock; } server { listen 80; server_name nextcloud.domain.com; if ($host = nextcloud.domain.com) { return 301 https://$server_name:443$request_uri; } # managed by Certbot listen 443 ssl http2; ssl_certificate /etc/letsencrypt/live/nextcloud.domain.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/nextcloud.domain.com/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 add_header Strict-Transport-Security "max-age=31536000" always; # managed by Certbot ssl_trusted_certificate /etc/letsencrypt/live/nextcloud.domain.com/chain.pem; # managed by Certbot ssl_stapling on; # managed by Certbot ssl_stapling_verify on; # managed by Certbot # Add headers to serve security related headers # Before enabling Strict-Transport-Security headers please read into this # topic first. #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;"; # # 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 X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; add_header X-Download-Options noopen; add_header X-Permitted-Cross-Domain-Policies none; add_header Referrer-Policy no-referrer; # 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/nextcloud; location = /robots.txt { allow all; log_not_found off; access_log off; } # The following 2 rules are only needed for the user_webfinger app. # Uncomment it if you're planning to use this app. #rewrite ^/.well-known/host-meta /public.php?service=host-meta last; #rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last; # The following rule is only needed for the Social app. # Uncomment it if you're planning to use this app. #rewrite ^/.well-known/webfinger /public.php?service=webfinger last; location = /.well-known/carddav { return 301 $scheme://$host:$server_port/remote.php/dav; } location = /.well-known/caldav { return 301 $scheme://$host:$server_port/remote.php/dav; } # 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; # Uncomment if your server is build with the ngx_pagespeed module # This module is currently not supported. #pagespeed off; location / { rewrite ^ /index.php; } location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ { deny all; } location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) { deny all; } location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) { 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; # Avoid sending the security headers twice fastcgi_param modHeadersAvailable true; # Enable pretty urls fastcgi_param front_controller_active true; fastcgi_pass php-handler; fastcgi_intercept_errors on; fastcgi_request_buffering off; } location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) { try_files $uri/ =404; index index.php; } # Adding the cache control header for js, css and map files # Make sure it is BELOW the PHP block location ~ \.(?:css|js|woff2?|svg|gif|map)$ { try_files $uri /index.php$request_uri; add_header Cache-Control "public, max-age=15778463"; # Add headers to serve security related headers (It is intended to # have those duplicated to the ones above) # Before enabling Strict-Transport-Security headers please read into # this topic first. #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;"; # # 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 X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; add_header X-Download-Options noopen; add_header X-Permitted-Cross-Domain-Policies none; add_header Referrer-Policy no-referrer; # Optional: Don't log access to assets access_log off; } location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ { try_files $uri /index.php$request_uri; # Optional: Don't log access to other assets access_log off; } }
This is the curl output:
curl -i http://nextcloud.domain.com HTTP/1.1 301 Moved Permanently Date: Mon, 03 Feb 2020 20:54:01 GMT Transfer-Encoding: chunked Connection: keep-alive Cache-Control: max-age=3600 Expires: Mon, 03 Feb 2020 21:54:01 GMT Location: https://nextcloud.domain.com/ X-Content-Type-Options: nosniff Server: cloudflare CF-RAY: 55f74c364a04d8d1-AMS
And when I try to access the
https
version:curl -i https://nextcloud.domain.com HTTP/2 301 date: Mon, 03 Feb 2020 20:55:45 GMT content-type: text/html set-cookie: __cfduid=d160f56e9986d2c7037ff81e233fcad0e1580763345; expires=Wed, 04-Mar-20 20:55:45 GMT; path=/; domain=.domain.com; HttpOnly; SameSite=Lax; Secure location: https://nextcloud.domain.com:443/ strict-transport-security: max-age=15552000; includeSubDomains; preload x-content-type-options: nosniff x-xss-protection: 1; mode=block x-robots-tag: none x-download-options: noopen x-permitted-cross-domain-policies: none referrer-policy: no-referrer cf-cache-status: DYNAMIC expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" server: cloudflare cf-ray: 55f74ebf5ed62bfe-AMS <html> <head><title>301 Moved Permanently</title></head> <body bgcolor="white"> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx/1.14.2</center> </body> </html>
I have replaced my domain name with
domain
.In the
/var/log/nginx/access.log
I am getting a lot of lines with the redirect and HTTP codes: 301 and 185.If I delete the redirect
return 301 https://$server_name:443@request_uri;
line I am still getting redirected tohttps
and I don't have any idea why.For the record, I used to have
lighttpd
in the past installed on the Raspberry Pi but I have uninstalled it before installing theNGINX
. - I have created an
-
Georgi Stoyanov over 4 yearsactually, I have just fixed my issue and it was related to the Cloudflare DNS configuration. There I have enabled re-write HTTP to HTTPS and this caused the problem. Additionally, I have enabled the DNS to be proxied. Since I fixed it I am not experiencing any more redirects. Plus listening on both ports in the same server block is also OK according to the official
NGINX
documentation: nginx.org/en/docs/http/… . If I check the output with curl I am seeing one 301 redirect HTTP->HTTPS and then 302 to the login page and 200 for login -
Piotr P. Karwasz over 4 yearsYou can't configure a
HTTP
redirect withDNS
so Cloudflare probably points your domain to their servers, which act as a reverse proxy. Iscloudflare.domain.com
pointing to your IP? If that were the case your configuration would cause a redirect loop. I edited the answer with more details. -
Georgi Stoyanov over 4 yearsThis is exactly what happened, yes, they were proxying the requests to my domain through their IPs