# Map to check if client is capable of handling webp map $http_accept $webp_suffix { default ""; "~*webp" ".webp"; # Sets suffix to .webp if Accept header contains webp } # Map to capture the image path *without* the file extension # This regex captures everything before the last dot and jpg/jpeg/png extension map $uri $image_path_without_ext { ~^(?.+)\.(?:jpe?g|png)$ $captured_path; } server { listen 80; listen [::]:80; server_name localhost; # Replace localhost with your actual domain if needed # Define the root directory for your website files root /usr/share/nginx/html; # Location block specifically for JPG, JPEG, and PNG images location ~* \.(jpe?g|png)$ { # Add Vary header tells caches that response depends on Accept header add_header Vary Accept; # Set cache expiration headers for images expires 180d; add_header Pragma "public"; add_header Cache-Control "public"; # Try to serve the .webp file first if browser supports it # $image_path_without_ext comes from the map above # $webp_suffix comes from the map above (.webp or empty) # If $image_path_without_ext$webp_suffix exists (e.g., /path/image.webp), serve it. # Otherwise, try the original $uri (e.g., /path/image.png). # If neither exists, return 404. try_files $image_path_without_ext$webp_suffix $uri =404; } # Default location block for other requests (HTML, CSS, JS, etc.) location / { index index.html index.htm; try_files $uri $uri/ /index.html; # Common pattern for SPAs/frameworks } # Error pages error_page 404 /404.html; location = /404.html { internal; # Prevents direct access to the error page } error_page 500 502 503 504 /50x.html; # Optional: generic 50x error page location = /50x.html { internal; } # Gzip settings (keep as they are) gzip on; gzip_comp_level 4; gzip_types text/html text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; }