How to Serve Pre-Gzipped Files with Proper Content-Type in Nginx for Browser Display


3 views

When serving pre-gzipped log files (e.g., access.log.gz) through Nginx, browsers typically download them as binary files rather than displaying their decompressed content. This occurs because Nginx sends these files with application/x-gzip as the default Content-Type.

To make browsers automatically decompress and display the content, you need to:

  1. Set the correct Content-Type header
  2. Ensure proper Content-Encoding
location ~* \.gz$ {
    add_header Content-Encoding gzip;
    types { text/plain gz; }
    default_type text/plain;
}

For log files specifically, consider this production-tested configuration:

server {
    listen 80;
    server_name logs.example.com;

    location / {
        root /var/log/nginx;
        
        location ~* \.(log|txt)\.gz$ {
            add_header Content-Encoding gzip;
            types { text/plain gz; }
            default_type text/plain;
            
            # Optional: Set caching headers
            expires 1h;
            add_header Cache-Control "public";
        }
    }
}

For more complex scenarios:

Handling Multiple File Types

location ~* \.(log|txt|csv)\.gz$ {
    add_header Content-Encoding gzip;
    types {
        text/plain gz;
        text/csv csv.gz;
        application/json json.gz;
    }
}

Security Implications

Remember to:

  • Restrict access to sensitive log files
  • Consider adding authentication
  • Set appropriate CORS headers if needed

Verify with curl:

curl -I http://yourserver/access.log.gz

Should return headers including:

Content-Type: text/plain
Content-Encoding: gzip

When dealing with pre-gzipped log files (or any .gz files) that need to be served as plain text, we face an interesting technical challenge. By default, browsers will either:

  • Download the .gz file as binary
  • Attempt to display raw compressed data if forced to render as text/plain

Here's the proper Nginx configuration to make .gz files display as decompressed text in browsers:

location ~* \.gz$ {
    gzip off;
    add_header Content-Encoding gzip;
    default_type text/plain;
}

Let's break down why this works:

gzip off;               # Disables on-the-fly gzipping
add_header Content-Encoding gzip;  # Tells browser this is gzipped content
default_type text/plain;   # Forces Content-Type header

For more complex scenarios, consider these variations:

# For specific file types
location ~* \.log\.gz$ {
    gzip off;
    add_header Content-Encoding gzip;
    types { } default_type text/plain;
}

# With cache control
location ~* \.gz$ {
    gzip off;
    add_header Content-Encoding gzip;
    default_type text/plain;
    expires 1h;
    add_header Cache-Control "public";
}

After implementing, verify with curl:

curl -I http://yourserver.com/example.log.gz

Look for these headers in response:

Content-Type: text/plain
Content-Encoding: gzip

When serving many gzipped files:

  • Ensure sendfile is enabled in nginx
  • Consider adding "gzip_static on" for fallback handling
  • Monitor memory usage when serving large .gz files