Last9 Last9

Mar 6th, ‘25 / 6 min read

Nginx Logging: A Complete Guide for Beginners

Learn how to set up, manage, and optimize Nginx logging for better debugging, monitoring, and performance insights in your applications.

Nginx Logging: A Complete Guide for Beginners

So, you're wrestling with Nginx logs, huh? Been there. In fact, I used to spend way too much time hunting down log files until I finally got smart about it. Let me save you the trouble.

Nginx logs are like the black box flight recorder for your web server. When everything crashes and burns (and it will), those logs are often the only evidence left to figure out what happened. But first, you need to know where to find them.

Where to Find Nginx Logs by Default

On most Linux setups, Nginx logs live in /var/log/nginx/. Shocking, I know. But here's the breakdown by OS:

Ubuntu/Debian:

/var/log/nginx/access.log
/var/log/nginx/error.log

CentOS/RHEL:

/var/log/nginx/access.log
/var/log/nginx/error.log

FreeBSD:

/var/log/nginx-access.log
/var/log/nginx-error.log

Windows: (Yes, some brave souls run Nginx on Windows)

nginx/logs/access.log
nginx/logs/error.log

Docker Containers:

/var/log/nginx/access.log
/var/log/nginx/error.log

I once spent an entire afternoon debugging an API that was mysteriously failing in production. Turns out Nginx was rejecting the requests with a 413 error because the payload was too large. A simple cat /var/log/nginx/error.log would have shown me that in seconds. Don't be like me - check the nginx log location first.

💡
If you're working with Nginx logs, understanding error logs is just as important. This guide breaks down how to find and fix common errors.

Where Are Your Nginx Logs Stored?

Maybe you're on a server someone else set up, and things aren't where they should be. No problem. Here's how to track down your nginx log location:

grep -r "access_log\|error_log" /etc/nginx/

Or go straight to the main config:

cat /etc/nginx/nginx.conf | grep "log"

For virtual hosts:

grep -r "access_log\|error_log" /etc/nginx/sites-available/

One thing I've learned the hard way: logs can be defined at multiple levels. You might have global logs in the main config, plus different logs for each virtual host. It's logs all the way down!

How to Customize Your Log Locations

Once you know where your logs are, you might want to move them. Maybe you want logs organized by project, or you want to store them on a separate partition. Whatever the reason, here's how:

http {
    # Global log settings
    access_log /path/to/custom/access.log;
    error_log /path/to/custom/error.log warn;
    
    server {
        # Server-specific log settings (overrides global)
        access_log /path/to/site-specific/access.log;
        error_log /path/to/site-specific/error.log;
        
        # You can even disable logging for certain locations
        location /health-check {
            access_log off;
            # health checks don't need to fill up your logs
        }
    }
}

I manage several sites on a single server, and I've found it incredibly helpful to organize logs by project:

access_log /var/log/nginx/projectname-access.log;
error_log /var/log/nginx/projectname-error.log;

This way, when something breaks at 2 AM (and it will), I'm not sifting through logs for a dozen different sites.

💡
Keeping track of Nginx logs is key to understanding your server’s health. This guide walks through how to monitor Nginx effectively.

Breaking Down Nginx Log Formats

Now that you know the nginx log location, let's talk about what's actually in those logs.

The default access log format (called "combined") looks like this:

log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

Which produces entries like:

192.168.1.1 - - [03/Mar/2025:15:42:31 +0000] "GET /api/users HTTP/1.1" 200 1234 "https://example.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"

That's fine for basic stuff, but as your site grows, you'll want more info. Here's a custom format I use for performance debugging:

log_format detailed '$remote_addr - $remote_user [$time_local] '
                     '"$request" $status $body_bytes_sent '
                     '"$http_referer" "$http_user_agent" '
                     '$request_time $upstream_response_time $pipe';

access_log /var/log/nginx/detailed-access.log detailed;

That $request_time field has saved my bacon more times than I can count. When users complain that "the site is slow," you can quickly find out which requests are taking forever.

Practical Techniques for Better Log Analysis

Having logs is one thing. Making sense of them is another. Here are some tricks I use daily:

Find all 500 errors from the last hour:

grep " 5[0-9][0-9] " /var/log/nginx/access.log | grep "$(date -d '1 hour ago' +'%d/%b/%Y:%H')"

Identify slow requests (taking more than 2 seconds):

awk '$NF > 2.0 {print $0}' /var/log/nginx/access.log

Find your top 10 visitors:

cat /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -n 10

See who's hammering a specific URL:

grep "GET /specific-url" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -n

For day-to-day log analysis, I'm a big fan of GoAccess:

goaccess /var/log/nginx/access.log -c

It gives you a real-time dashboard right in your terminal. Much nicer than squinting at raw logs.

💡
Analyzing logs is just one part of the equation—monitoring them in real time helps catch issues early. This guide covers how to do it.

How to Handle Log Rotation and Management

Here's something they don't tell you when you're setting up your first server: logs grow. And grow. And grow.

I once got a frantic call at 3 AM because a production server was unresponsive. The culprit? The root partition was 100% full because nobody had set up log rotation. Don't be that person.

Most Linux distributions include logrotate. Here's a sane configuration for your nginx log location:

/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        if [ -d /etc/nginx ]; then
            if [ -f /var/run/nginx.pid ]; then
                kill -USR1 `cat /var/run/nginx.pid`
            fi
        fi
    endscript
}

This rotates logs daily, keeps two weeks of history, and compresses old logs to save space. The kill -USR1 bit tells Nginx to reopen its log files without restarting.

Fixing Common Problems in Nginx Logging

Over the years, I've run into pretty much every possible log-related issue. Here are the greatest hits:

Logs Not Being Written

If you've configured a custom log path but nothing's showing up:

Test if the Nginx user can actually write there:

sudo -u www-data touch /path/to/logs/test.log

Make sure parent directories exist:

sudo mkdir -p /path/to/logs

Check permissions. Nginx runs as www-data or nginx user, and needs write access:

sudo chown -R www-data:www-data /path/to/logs
sudo chmod 755 /path/to/logs

I once spent two hours debugging why logs weren't being written, only to discover that I'd mounted a read-only filesystem at the log path. Don't do that.

💡
Managing log files is just as important as collecting them. This guide explains how log rotation works in Linux to keep things organized.

Missing Log Entries

Sometimes, you're sure something happened, but it's not in the logs:

  1. Internal redirects might only log the initial request
  2. If you've set access_log off for certain locations, those requests won't be logged
  3. Nginx buffers log writes, so entries might be delayed

Incorrect Log Format

If your logs look weird:

  1. Make sure your log_format directive comes before any access_log directives that use it
  2. Check that the format name matches what you're using in access_log
  3. Restart Nginx after changing log formats (reload isn't always enough)

Setting Up Advanced Logging in Nginx

Once you've mastered the basics, here are some power-user techniques:

JSON Logging

For easier parsing by log management systems:

log_format json_combined escape=json '{"time_local":"$time_local",'
                       '"remote_addr":"$remote_addr",'
                       '"remote_user":"$remote_user",'
                       '"request":"$request",'
                       '"status": "$status",'
                       '"body_bytes_sent":"$body_bytes_sent",'
                       '"request_time":"$request_time",'
                       '"http_referrer":"$http_referer",'
                       '"http_user_agent":"$http_user_agent"}';

access_log /var/log/nginx/access.log json_combined;

This makes it much easier to ship logs to systems like Elasticsearch or Splunk.

Conditional Logging

Why log everything when you only care about errors?

map $status $loggable {
    ~^[23]  0;
    default 1;
}

access_log /var/log/nginx/error-access.log combined if=$loggable;

This only logs requests that don't return 2xx or 3xx status codes. Your disk will thank you.

Logging to Syslog

For centralized logging:

error_log syslog:server=unix:/dev/log,facility=local7,tag=nginx,severity=info;
access_log syslog:server=unix:/dev/log,facility=local7,tag=nginx,severity=info combined;

This sends logs to syslog, which can forward them to a central log server. Great for multi-server setups.

💡
Working with Nginx logs often means using Linux commands. This cheat sheet has useful commands to make the job easier.

The Bottom Line

Look, mastering your nginx log location and configuration isn't the sexiest part of web development. But when things go wrong (and they will), you'll be glad you know where to look.

I've been building web applications for over a decade, and I still check the logs first when something breaks. They're often the difference between a quick fix and an all-night debugging session.

Take the time to set up proper logging, rotation, and analysis tools. Your future self, awakened at 3 AM by a production alert, will thank you.

Additional Resources

Contents


Newsletter

Stay updated on the latest from Last9.

Authors
Aditya Godbole

Aditya Godbole

CTO at Last9

Topics