Rotating Nginx access and error logs keeps /var/log from growing until the filesystem fills, while preserving enough history to troubleshoot outages, investigate suspicious traffic, and compare performance over time.

Most Linux distributions rely on logrotate to rename and optionally compress log files on a schedule (often via cron or a systemd timer). Because Nginx keeps log files open, rotation must be followed by a reopen signal so the master process closes old file descriptors and resumes writing to the active log paths.

Retention, rotation frequency, and compression settings should match traffic volume and incident-response needs. Incorrect file patterns, permissions, or ownership can leave logs unrotated or stop logging entirely after a rotation, so a dry-run plus a one-time forced rotation provides a safe validation path before relying on the policy in production.

Steps to configure log rotation for Nginx:

  1. Locate the logrotate configuration for Nginx.
    $ ls -la /etc/logrotate.d/nginx
    -rw-r--r-- 1 root root 329 Nov 30  2023 /etc/logrotate.d/nginx
  2. Confirm the Nginx log file paths matched by the rotation rule.
    $ sudo grep -R --line-number -E '^[[:space:]]*(access_log|error_log)[[:space:]]' /etc/nginx/nginx.conf /etc/nginx/conf.d /etc/nginx/sites-enabled 2>/dev/null
    /etc/nginx/nginx.conf:4:error_log /var/log/nginx/error.log;
    /etc/nginx/nginx.conf:40:  access_log /var/log/nginx/access.log;

    Custom access_log or error_log paths require updating the logrotate glob (for example, changing /var/log/nginx/*.log).

  3. Review or create a rotation policy that fits the environment.
    /var/log/nginx/*.log {
        daily
        missingok
        rotate 14
        compress
        delaycompress
        notifempty
        create 0640 www-data adm
        sharedscripts
        prerotate
            if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
                run-parts /etc/logrotate.d/httpd-prerotate; \
            fi \
        endscript
        postrotate
            invoke-rc.d nginx rotate >/dev/null 2>&1
        endscript
    }

    Nginx must reopen logs after rotation. The default policy uses invoke-rc.d nginx rotate to signal the master process; if you customize this file, keep an equivalent reopen command and a create line with the correct user/group (commonly www-data or nginx). Avoid copytruncate on busy servers, since it can drop log lines during truncation.

  4. Test the rotation configuration in debug mode.
    $ sudo logrotate -d /etc/logrotate.d/nginx
    warning: logrotate in debug mode does nothing except printing debug messages!  Consider using verbose mode (-v) instead if this is not what you want.
    
    reading config file /etc/logrotate.d/nginx
    Reading state from file: /var/lib/logrotate/status
    state file /var/lib/logrotate/status does not exist
    ##### snipped #####
    considering log /var/log/nginx/access.log
    Creating new state
      Now: 2025-12-29 21:50
      Last rotated at 2025-12-29 21:00
      log does not need rotating (log has already been rotated)
    ##### snipped #####
  5. Force a rotation to validate the policy end-to-end.
    $ sudo logrotate -vf /etc/logrotate.d/nginx
    reading config file /etc/logrotate.d/nginx
    Creating stub state file: /var/lib/logrotate/status
    acquired lock on state file /var/lib/logrotate/statusReading state from file: /var/lib/logrotate/status
    ##### snipped #####
    rotating pattern: /var/log/nginx/*.log  forced from command line (14 rotations)
    ##### snipped #####
    running prerotate script
    renaming /var/log/nginx/access.log to /var/log/nginx/access.log.1
    creating new /var/log/nginx/access.log mode = 0640 uid = 33 gid = 4
    running postrotate script
    ##### snipped #####

    Force rotation changes files immediately, so run it during a maintenance window on busy servers.

  6. List the rotated log files to confirm retention/compression behavior.
    $ ls -lh /var/log/nginx
    total 4.0K
    -rw-r----- 1 www-data adm   0 Dec 29 21:50 access.log
    -rw-r----- 1 www-data adm 518 Dec 29 21:49 access.log.1
    -rw-r----- 1 www-data adm   0 Dec 29 21:46 error.log
  7. Generate a request to create a new access log entry.
    $ curl -I --silent http://127.0.0.1/
    HTTP/1.1 200 OK
    Server: nginx/1.24.0 (Ubuntu)
    Date: Mon, 29 Dec 2025 21:50:42 GMT
    Content-Type: text/html
    Content-Length: 10671
    Connection: keep-alive
    ETag: "6950cb18-29af"
    X-Frame-Options: SAMEORIGIN
    X-Content-Type-Options: nosniff
    Referrer-Policy: strict-origin-when-cross-origin
    ##### snipped #####
  8. Confirm that Nginx is writing to the active log files.
    $ sudo tail -n 5 /var/log/nginx/access.log
    127.0.0.1 - - [29/Dec/2025:21:51:55 +0000] "GET / HTTP/1.1" 200 10671 "-" "curl/8.5.0"
    127.0.0.1 - - [29/Dec/2025:21:52:06 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/8.5.0"
    ##### snipped #####