# ============================================================================ # Full nginx.conf for Nextcloud running in Docker BEHIND an external # SSL-terminating reverse proxy (). # # - 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; # 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 { # Include standard MIME types include /etc/nginx/mime.types; default_type application/octet-stream; # Custom MIME types - Enhanced for MJS support types { image/x-raw raw; image/x-sony-arw arw; application/javascript mjs; # Ensure MJS has proper MIME type } # 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 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; client_body_buffer_size 512k; # Buffer for client request body # --- Gzip Compression --- gzip on; gzip_vary on; 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; # --- 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; # --- 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) { 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; } # Enhanced handling for JavaScript module files (.mjs) location ~* \.mjs$ { # Multiple methods to ensure correct Content-Type header types { application/javascript mjs; } # Force the correct MIME type default_type application/javascript; # Explicitly set Content-Type header add_header Content-Type application/javascript always; # 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; } # Main PHP processing block location ~ \.php(?:$|/) { # 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; # 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; # --- 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 (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; # 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; } } # 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; # 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; } # 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; } } # End server block (port 80) } # End http block