--- # Nginx reverse proxy for Nextcloud - name: Create certbot webroot file: path: /var/www/certbot state: directory owner: www-data group: www-data mode: "0755" - name: Deploy HTTP-only nginx config (for certbot) copy: dest: /etc/nginx/sites-available/nextcloud content: | server { listen 80; server_name {{ nextcloud_domain }}; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 200 'phantom setup in progress'; add_header Content-Type text/plain; } } mode: "0644" notify: reload nginx - name: Enable Nextcloud nginx site file: src: /etc/nginx/sites-available/nextcloud dest: /etc/nginx/sites-enabled/nextcloud state: link notify: reload nginx - name: Flush handlers to apply HTTP config meta: flush_handlers - name: Setup TLS (certbot with self-signed fallback) include_tasks: ../../common/tls_setup.yml vars: _tls_domain: "{{ nextcloud_domain }}" - name: Deploy SSL nginx config copy: dest: /etc/nginx/sites-available/nextcloud content: | upstream php-handler { server unix:/run/php/php{{ php_ver }}-fpm-nextcloud.sock; } server { listen 80; server_name {{ nextcloud_domain }}; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; server_name {{ nextcloud_domain }}; ssl_certificate {{ _ssl_cert }}; ssl_certificate_key {{ _ssl_key }}; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; server_tokens off; {% if _is_selfsigned | default(false) | bool %} add_header Strict-Transport-Security "max-age=0" always; {% else %} add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; {% endif %} add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options SAMEORIGIN always; add_header X-Robots-Tag "noindex, nofollow" always; add_header Referrer-Policy no-referrer always; client_max_body_size 10G; client_body_timeout 3600s; fastcgi_buffers 64 4K; root /var/www/nextcloud; location = /.well-known/carddav { return 301 $scheme://$host/remote.php/dav; } location = /.well-known/caldav { return 301 $scheme://$host/remote.php/dav; } location = /.well-known/webfinger { return 301 $scheme://$host/index.php/.well-known/webfinger; } location = /.well-known/nodeinfo { return 301 $scheme://$host/index.php/.well-known/nodeinfo; } 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\/.+|ocs-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; fastcgi_param modHeadersAvailable true; fastcgi_param front_controller_active true; fastcgi_pass php-handler; fastcgi_intercept_errors on; fastcgi_request_buffering off; fastcgi_read_timeout 3600; } location ~ ^\/(?:updater|ocs-provider)(?:$|\/) { try_files $uri/ =404; index index.php; } location ~ \.(?:css|js|woff2?|svg|gif|map)$ { try_files $uri /index.php$request_uri; add_header Cache-Control "public, max-age=15778463"; access_log off; } location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$ { try_files $uri /index.php$request_uri; access_log off; } } mode: "0644" notify: reload nginx