diff --git a/nginx.conf b/nginx.conf index 5c6f020..b8bd81f 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,164 +1,306 @@ +# ============================================================================ +# Full nginx.conf for Nextcloud running in Docker BEHIND an external +# SSL-terminating reverse proxy (at 192.168.0.215). +# +# - This Nginx instance listens on HTTP/80. +# - It serves Nextcloud directly using PHP-FPM from the 'nextcloud:9000' container. +# - It trusts headers forwarded by the external proxy. +# - NO SSL configuration is needed here. +# ============================================================================ + user www-data; +# Use 'auto' or set to the number of CPU cores available to the container worker_processes auto; -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; + +# Standard error log and PID file paths within the container +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; events { + # Adjust worker_connections based on expected load worker_connections 1024; } http { - # Basic settings - include /etc/nginx/mime.types; - default_type application/octet-stream; - server_tokens off; - - # Additional MIME types + # Include standard MIME types + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Custom MIME types - Enhanced for MJS support types { - application/javascript mjs; + image/x-raw raw; + image/x-sony-arw arw; + application/javascript mjs; # Ensure MJS has proper MIME type } - - # Logging - access_log /var/log/nginx/access.log combined; - error_log /var/log/nginx/error.log error; - - # Gzip compression - gzip on; - gzip_vary on; - gzip_comp_level 4; - gzip_min_length 256; - gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon; - - # Upstream PHP handler + + # Double-check the MIME type mapping by adding it globally + map $uri $javascript_module { + ~\.mjs$ "application/javascript"; + default ""; + } + + # ================================================== + # Proxy Handling & Real IP Configuration + # ================================================== + # Define the IP of the trusted external reverse proxy + set_real_ip_from 192.168.0.215; + # Specify the header containing the client's real IP + real_ip_header X-Forwarded-For; + # Use 'on' if the proxy might add multiple IPs (e.g., X-Forwarded-For: client, proxy1) + # real_ip_recursive on; + + # ================================================== + # Logging Configuration + # ================================================== + # Log format including the forwarded-for header. + # If realip module works, $remote_addr will show the real client IP. + 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; + + # ================================================== + # Performance & Security Defaults + # ================================================== + server_tokens off; # Hide nginx version + sendfile on; # Use efficient file transfer + tcp_nopush on; # Optimize packet sending + tcp_nodelay on; # Reduce latency for keep-alive + keepalive_timeout 65; # Keep connections open longer + + # Helper map for cache control (retained from original) + map $arg_v $asset_immutable { + "" ""; + default ", immutable"; + } + + # ================================================== + # Upstream PHP-FPM Definition + # ================================================== + # Define the Nextcloud application container running PHP-FPM upstream php-handler { + # Use the Docker service name and port server nextcloud:9000; } - + + # ================================================== + # Main Server Block (Listens on HTTP/80) + # ================================================== server { - listen 80 default_server; - listen [::]:80 default_server; - server_name gabenszip.com; - - # Path to webroot - root /var/www/html; - - # Max upload size - client_max_body_size 10G; + # Listen on port 80 within the container network + listen 80; + # listen [::]:80; # Uncomment if using IPv6 internally + + # Optional: Set server name (domain used publicly) + # server_name cloud.example.com; # <<< Your public Nextcloud domain + + # --- Basic Settings --- + # Set maximum allowed size for uploaded files + client_max_body_size 512M; # Adjust as needed + # Set timeout for reading client request body client_body_timeout 300s; + # FastCGI buffer settings for PHP communication fastcgi_buffers 64 4K; - - # Enable gzip but do not remove ETag headers + client_body_buffer_size 512k; # Buffer for client request body + + # --- Gzip Compression --- 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; - - # HTTP response headers - 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 "noindex, nofollow" always; - add_header X-XSS-Protection "1; mode=block" always; - - # Remove X-Powered-By, which is an information leak + gzip_comp_level 4; # Balance between CPU and compression ratio + gzip_min_length 256; # Don't compress very small files + gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; # Compress even for proxied requests + # Define types of files to compress + 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/wasm 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; + + # --- Security Headers --- + # These add defense-in-depth. HSTS should be set on the external proxy (192.168.0.215). + # add_header Strict-Transport-Security "max-age=..." always; # DO NOT set HSTS here + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "noindex, nofollow" always; + add_header X-XSS-Protection "1; mode=block" always; + # Content Security Policy (retained from original - review if suitable) + add_header Content-Security-Policy "frame-ancestors 'self'; connect-src 'self' blob: stun.nextcloud.com:443 wss://gabenszip.com https://gabenszip.com http://gabenszip.com ws://gabenszip.com gabenszip.com:* 'unsafe-eval' 'unsafe-inline'" always; + + # Hide PHP's "X-Powered-By" header fastcgi_hide_header X-Powered-By; - - # Specify how to handle directories -- first look for an index.html file + + # --- Document Root --- + # IMPORTANT: Adjust this path if your Nextcloud files are mounted elsewhere in this container + root /var/www/html; + # Define default index files index index.php index.html /index.php$request_uri; - - # Rule borrowed from `.htaccess` to handle Microsoft DAV clients + + # --- Location Blocks --- + + # Deny access to sensitive files/directories + location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; } + location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; } + + # Handle /.well-known requests for service discovery (CalDAV, CardDAV, etc.) + location ^~ /.well-known { + location = /.well-known/carddav { return 301 /remote.php/dav/; } + location = /.well-known/caldav { return 301 /remote.php/dav/; } + # ACME challenges usually handled by external proxy, otherwise allow here if needed + location ~ ^/\.well-known/(acme-challenge|pki-validation)/ { allow all; } + # Let Nextcloud handle other .well-known requests via PHP + return 301 /index.php$request_uri; + } + + # Special handling for Microsoft DAV clients hitting the root location = / { - if ( $http_user_agent ~ ^DavClnt ) { + if ($http_user_agent ~ ^DavClnt) { return 302 /remote.php/webdav/$is_args$args; } + # If not DAV, fallback to PHP front controller + try_files $uri $uri/ /index.php$request_uri; } - + + # Robots.txt - allow access location = /robots.txt { allow all; log_not_found off; access_log off; } - - # 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 rules in this block are an adaptation of the rules - # in `.htaccess` that concern `/.well-known`. + + # Enhanced handling for JavaScript module files (.mjs) + location ~* \.mjs$ { + # Multiple methods to ensure correct Content-Type header + types { application/javascript mjs; } - location = /.well-known/carddav { return 301 /remote.php/dav/; } - location = /.well-known/caldav { return 301 /remote.php/dav/; } + # Force the correct MIME type + default_type application/javascript; - location /.well-known/acme-challenge { try_files $uri $uri/ =404; } - location /.well-known/pki-validation { try_files $uri $uri/ =404; } + # Explicitly set Content-Type header + add_header Content-Type application/javascript always; - # Let Nextcloud's API for `/.well-known` URIs handle all other - # requests by passing them to the front-end controller. - return 301 /index.php$request_uri; + # Override any default Content-Type + if ($javascript_module) { + add_header Content-Type $javascript_module always; + } + + # Try serving file directly, else pass to PHP + try_files $uri /index.php$request_uri; + + # Caching headers + add_header Cache-Control "public, max-age=15778463$asset_immutable"; + + # Add security headers + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "noindex, nofollow" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Content-Security-Policy "frame-ancestors 'self'; connect-src 'self' blob: stun.nextcloud.com:443 wss://gabenszip.com https://gabenszip.com http://gabenszip.com ws://gabenszip.com gabenszip.com:* 'unsafe-eval' 'unsafe-inline'" always; + + # Disable access logging for static files + access_log off; } - - # 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; } - - # Ensure this block, which passes PHP files to the PHP process, is above the blocks - # which handle static assets (as seen below). If this block is not declared first, - # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php` - # to the URI, resulting in a HTTP 500 error response. + + # Main PHP processing block location ~ \.php(?:$|/) { - # Required for legacy support - rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri; - + # Security rewrite: Prevent direct access to PHP files except known endpoints + rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri; + + # Split path info for PHP (e.g., /index.php/foo/bar) fastcgi_split_path_info ^(.+?\.php)(/.*)$; set $path_info $fastcgi_path_info; - + + # Try the script itself, otherwise return 404 try_files $fastcgi_script_name =404; - + + # Include standard FastCGI parameters include fastcgi_params; + + # --- Crucial FastCGI Parameters --- + # Set script filename and path info 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 + # HTTPS handling + fastcgi_param HTTPS off; # Let Nextcloud config handle the protocol + + # Force protocol settings to use HTTP (not HTTPS) + fastcgi_param HTTP_X_FORWARDED_PROTO http; + fastcgi_param REQUEST_SCHEME http; + + # Pass the client's real IP address to PHP + fastcgi_param REMOTE_ADDR $remote_addr; + + # Pass other useful headers from the proxy (ensure proxy sets them!) + fastcgi_param HTTP_X_FORWARDED_FOR $http_x_forwarded_for; + + # Pass host header + fastcgi_param HTTP_HOST $http_host; + + # Parameters for Nextcloud pretty URLs and header management + fastcgi_param modHeadersAvailable true; + fastcgi_param front_controller_active true; + + # Send request to the PHP-FPM upstream handler defined earlier fastcgi_pass php-handler; - - fastcgi_intercept_errors on; - fastcgi_request_buffering off; - - fastcgi_max_temp_file_size 0; - fastcgi_read_timeout 3600; + + # --- Performance & Error Handling --- + fastcgi_intercept_errors on; # Let Nginx handle PHP errors (e.g., show custom 50x page) + fastcgi_request_buffering off; # Disable buffering for long-running scripts/uploads + fastcgi_max_temp_file_size 0; # Prevent writing large uploads to temp files } - - # Serve static files - location ~ \.(?:css|js|mjs|svg|gif|png|jpg|ico|wasm|tflite|map)$ { + + # Serve static files (CSS, JS, images, including custom types) + location ~* \.(?:css|js|svg|gif|ico|jpe?g|png|webp|wasm|tflite|map|ogg|flac|arw|raw)$ { + # Try to serve file directly, else pass to PHP (e.g., for theming) try_files $uri /index.php$request_uri; - add_header Cache-Control "public, max-age=15778463"; - access_log off; # Optional: Don't log access to assets - + # Caching headers + add_header Cache-Control "public, max-age=15778463$asset_immutable"; + # Add security headers (can potentially be inherited) + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "noindex, nofollow" always; + add_header X-XSS-Protection "1; mode=block" always; + # Disable access logging for static files + access_log off; + + # Specific MIME type for WebAssembly if needed explicitly location ~ \.wasm$ { default_type application/wasm; } } - - location ~ \.woff2?$ { + + # Serve font files with longer expiry + location ~ \.(?:woff2?|ttf|eot)$ { + # Try to serve file directly, else pass to PHP 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 + # Caching headers (fonts rarely change) + add_header Cache-Control "public, max-age=15778463$asset_immutable"; + expires 7d; # Alternative cache control + # Add security headers (can potentially be inherited) + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + # Disable access logging for static files + access_log off; } - - # Rule borrowed from `.htaccess` + + # Redirect for /remote endpoint used by some clients location /remote { return 301 /remote.php$request_uri; } - + + # Fallback location - pass everything else to the PHP front controller location / { try_files $uri $uri/ /index.php$request_uri; } - } -} \ No newline at end of file + + } # End server block (port 80) +} # End http block \ No newline at end of file