Nextcloud Nginx Reverse Proxy

From Drake Panzer

Config for running Nextcloud behind a Nginx reverse proxy.

upstream backend_hosts
{
  server 192.168.0.123:443;
}

server
{
  listen 80;
  listen [::]:80;
  server_name nc.example.com;
  return 301 https://$server_name$request_uri;
}

server
{
  listen 443 ssl;
  listen [::]:443 ssl;
  server_name nc.example.com;

  access_log /var/log/nginx/nc-example.log;

  # proxy config
  proxy_ssl_verify off; # dont check if the proxied server has a valid ssl cert
  # increase the proxy timeout for long file operations
  proxy_connect_timeout 600;
  proxy_send_timeout 600;
  proxy_read_timeout 36000s;
  send_timeout 600;

  # send all traffic to the backend server
  location /
  {
    proxy_pass https://backend_hosts;
    include /etc/nginx/includes/nc-proxy.conf; # set special proxy headers
  }

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

  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;

  # proxy robots.txt
  location = /robots.txt
  {
    allow all;
    log_not_found off;
    proxy_pass https://backend_hosts/robots.txt;
    include /etc/nginx/includes/nc-proxy.conf;
  }

  # 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 ^~ /.well-known
  {
    # The following 6 rules are borrowed from `.htaccess`

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

  # Rules borrowed from `.htaccess` to hide certain paths from clients
  location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)
  {
    return 404;
  }
  location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)
  {
    return 404;
  }

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

  # proxy some special urls to the backend server, these may not be caught by the default location
  location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy)\.php(?:$|\/)
  {
    proxy_pass https://backend_hosts;
    include /etc/nginx/includes/nc-proxy.conf;
  }
  location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/)
  {
    proxy_pass https://backend_hosts;
    include /etc/nginx/includes/nc-proxy.conf;
  }

  # cache control for files
  location ~ \.(?:css|js|woff2?|svg|gif|map)$
  {
    proxy_pass https://backend_hosts;
    add_header Cache-Control "public, max-age=15778463";
    expires 6M; # Cache-Control policy borrowed from `.htaccess`
    include /etc/nginx/includes/nc-proxy.conf;
  }
  location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$
  {
    proxy_pass https://backend_hosts;
    expires 7d; # Cache-Control policy borrowed from `.htaccess`
    include /etc/nginx/includes/nc-proxy.conf;
  }

  # Certbot SSL stuff goes here
}


You also need this included file:

/etc/nginx/includes/nc-proxy.conf

proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
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 https;