Back to Logging guides

How to View and Configure NGINX Access & Error Logs

Ayooluwa Isaiah
Updated on October 12, 2023

In this tutorial, you will learn everything you need to know about logging in NGINX and how it can help you troubleshoot and quickly resolve any problem you may encounter on your web server. We will discuss where the logs are stored and how to access them, how to customize their format, and how to centralize them in one place with Syslog or a log management service.

Here's an outline of what you will learn by following through with this tutorial:

  • Where NGINX logs are stored and how to access them.
  • How to customize the NGINX log format and storage location to fit your needs.
  • How to utilize a structured format (such as JSON) for your NGINX logs.
  • How to centralize NGINX logs through Syslog or a managed cloud-based service.

Prerequisites

To follow through with this tutorial, you need the following:

  • A Linux server that includes a non-root user with sudo privileges. We tested the commands shown in this guide on an Ubuntu 20.04 server.
  • The NGINX web server installed  and enabled on your server.

Side note: Get an Nginx logs dashboard

Save hours of sifting through Nginx logs. Centralize with Better Stack and start visualizing your log data in minutes.

See the Nginx demo dashboard live.

Step 1 — Locating the NGINX log files

NGINX writes logs of all its events in two different log files:

  • Access log: this file contains information about incoming requests and user visits.
  • Error log: this file contains information about errors encountered while processing requests, or other diagnostic messages about the web server.

The location of both log files is dependent on the host operating system of the NGINX web server and the mode of installation. On most Linux distributions, both files will be found in the /var/log/nginx/ directory as access.log and error.log, respectively.

A typical access log entry might look like the one shown below. It describes an HTTP GET request to the server for a favicon.ico file.

Output
217.138.222.101 - - [11/Feb/2022:13:22:11 +0000] "GET /favicon.ico HTTP/1.1" 404 3650 "http://135.181.110.245/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Safari/537.36" "-"

Similarly, an error log entry might look like the one below, which was generated due to the inability of the server to locate the favicon.ico file that was requested above.

Output
2022/02/11 13:12:24 [error] 37839#37839: *7 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 113.31.102.176, server: _, request: "GET /favicon.ico HTTP/1.1", host: "192.168.110.245:80"

In the next section, you'll see how to view both NGINX log files from the command line.

Step 2 — Viewing the NGINX log files

Examining the NGINX logs can be done in a variety of ways. One of the most common methods involves using the tail command to view logs entries in real-time:

 
sudo tail -f /var/log/nginx/access.log

You will observe the following output:

Output
107.189.10.196 - - [14/Feb/2022:03:48:55 +0000] "POST /HNAP1/ HTTP/1.1" 404 134 "-" "Mozila/5.0"
35.162.122.225 - - [14/Feb/2022:04:11:57 +0000] "GET /.env HTTP/1.1" 404 162 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0"
45.61.172.7 - - [14/Feb/2022:04:16:54 +0000] "GET /.env HTTP/1.1" 404 197 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
45.61.172.7 - - [14/Feb/2022:04:16:55 +0000] "POST / HTTP/1.1" 405 568 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
45.137.21.134 - - [14/Feb/2022:04:18:57 +0000] "GET /dispatch.asp HTTP/1.1" 404 134 "-" "Mozilla/5.0 (iPad; CPU OS 7_1_2 like Mac OS X; en-US) AppleWebKit/531.5.2 (KHTML, like Gecko) Version/4.0.5 Mobile/8B116 Safari/6531.5.2"
23.95.100.141 - - [14/Feb/2022:04:42:23 +0000] "HEAD / HTTP/1.0" 200 0 "-" "-"
217.138.222.101 - - [14/Feb/2022:07:38:40 +0000] "GET /icons/ubuntu-logo.png HTTP/1.1" 404 197 "http://168.119.119.25/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Safari/537.36"
217.138.222.101 - - [14/Feb/2022:07:38:42 +0000] "GET /favicon.ico HTTP/1.1" 404 197 "http://168.119.119.25/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Safari/537.36"
217.138.222.101 - - [14/Feb/2022:07:44:02 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Safari/537.36"
217.138.222.101 - - [14/Feb/2022:07:44:02 +0000] "GET /icons/ubuntu-logo.png HTTP/1.1" 404 197 "http://168.119.119.25/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Safari/537.36"

The tail command prints the last 10 lines from the selected file. The -f option causes it to continue displaying subsequent lines that are added to the file in real-time.

To examine the entire contents of an NGINX log file, you can use the cat command or open it in your text editor:

 
sudo cat /var/log/nginx/error.log

If you want to filter the lines that contain a specific term, you can use the grep command as shown below:

 
sudo grep "GET /favicon.ico" /var/log/nginx/access.log

The command above will print all the lines that contain GET /favicon.ico so we can see how many requests were made for that resource.

Step 3 — Configuring NGINX access logs

The NGINX access log stores data about incoming client requests to the server which is beneficial when deciphering what users are doing in the application, and what resources are being requested. In this section, you will learn how to configure what data is stored in the access log.

One thing to keep in mind while following through with the instructions below is that you'll need to restart the nginx service after modifying the config file so that the changes can take effect.

 
sudo systemctl restart nginx

Enabling the access log

The NGINX access Log should be enabled by default. However, if this is not the case, you can enable it manually in the Nginx configuration file (/etc/nginx/nginx.conf) using the access_log directive within the http block.

/etc/nginx/nginx.conf
http {
  access_log /var/log/nginx/access.log;
}

This directive is also applicable in the server and location configuration blocks for a specific website:

/etc/nginx/nginx.conf
server {
   access_log /var/log/nginx/app1.access.log;

  location /app2 {
    access_log /var/log/nginx/app2.access.log;
  }
}

Disabling the access log

In cases where you'd like to disable the NGINX access log, you can use the special off value:

 
access_log off

You can also disable the access log on a virtual server or specific URIs by editing its server or location block configuration in the /etc/nginx/sites-available/ directory:

 
server {
  listen 80;

  access_log off;

  location ~* \.(woff|jpg|jpeg|png|gif|ico|css|js)$ {
    access_log off;
  }
}

Logging to multiple access log files

If you'd like to duplicate the access log entries in separate files, you can do so by repeating the access_log directive in the main config file or in a server block as shown below:

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

Don't forget to restart the nginx service afterward:

 
sudo systemctl restart nginx

Explanation of the default access log format

The access log entries produced using the default configuration will look like this:

Output
127.0.0.1 alice Alice [07/May/2021:10:44:53 +0200] "GET / HTTP/1.1" 200 396 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4531.93 Safari/537.36"

Here's a breakdown of the log message above:

  • 127.0.0.1: the IP address of the client that made the request.
  • alice: remote log name (name used to log in a user).
  • Alice: remote username (username of logged-in user).
  • [07/May/2021:10:44:53 +0200] : date and time of the request.
  • "GET / HTTP/1.1" : request method, path and protocol.
  • 200: the HTTP response code.
  • 396: the size of the response in bytes.
  • "-": the IP address of the referrer (- is used when the it is not available).
  • "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4531.93 Safari/537.36" - detailed user agent information.

Step 4 — Creating a custom log format

Customizing the format of the entries in the access log can be done using the log_format directive, and it can be placed in the http, server or location blocks as needed. Here's an example of what it could look like:

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

This yields a log entry in the following format:

Output
217.138.222.109 - - [14/Feb/2022:10:38:35 +0000] "GET /favicon.ico HTTP/1.1" 404 197 "http://192.168.100.1/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Safari/537.36"

The syntax for configuring an access log format is shown below. First, you need to specify a nickname for the format that will be used as its identifier, and then the log format string that represents the details and formatting for each log message.

 
log_format <nickname> '<formatting_variables>';

Here's an explanation of each variable used in the custom log format shown above:

  • $remote_addr: the IP address of the client
  • $remote_user: information about the user making the request
  • $time_local: the server's date and time.
  • $request: actual request details like path, method, and protocol.
  • $status: the response code.
  • $body_bytes_sent: the size of the response in bytes.
  • $http_referer: the IP address of the HTTP referrer.
  • $http_user_agent: detailed user agent information.

You may also use the following variables in your custom log format (see here for the complete list ):

  • $upstream_connect_time: the time spent establishing a connection with an upstream server.
  • $upstream_header_time: the time between establishing a connection and receiving the first byte of the response header from the upstream server.
  • $upstream_response_time: the time between establishing a connection and receiving the last byte of the response body from the upstream server.
  • $request_time: the total time spent processing a request.
  • $gzip_ratio: ration of gzip compression (if gzip is enabled).

After you create a custom log format, you can apply it to a log file by providing a second parameter to the access_log directive:

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

You can use this feature to log different information in to separate log files. Create the log formats first:

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

Then, apply them as shown below:

 
access_log /var/log/nginx/access.log custom;
access_log /var/log/nginx/agent_access.log agent;

This configuration ensures that user agent information for all incoming requests are logged into a separate access log file.

Step 5 - Formatting your access logs as JSON

A common way to customize NGINX access logs is to format them as JSON. This is quite straightforward to achieve by combining the log_format directive with the escape=json parameter introduced in Nginx 1.11.8 to escape characters that are not valid in JSON:

 
log_format custom_json 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"'
  '}';

After applying the custom_json format to a log file and restarting the nginx service, you will observe log entries in the following format:

Output
{
  "time_local": "14/Feb/2022:11:25:44 +0000",
  "remote_addr": "217.138.222.109",
  "remote_user": "",
  "request": "GET /icons/ubuntu-logo.png HTTP/1.1",
  "status": "404",
  "body_bytes_sent": "197",
  "request_time": "0.000",
  "http_referrer": "http://192.168.100.1/",
  "http_user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Safari/537.36"
}

Step 6 — Configuring NGINX error logs

Whenever NGINX encounters an error, it stores the event data in the error log so that it can be referred to later by a system administrator. This section will describe how to enable and customize the error logs as you see fit.

Enabling the error log

The NGINX error log should be enabled by default. However, if this is not the case, you can enable it manually in the relevant NGINX configuration file (either at the http, server, or location levels) using the error_log directive.

 
error_log /var/log/nginx/error.log;

The error_log directive can take two parameters. The first one is the location of the log file (as shown above), while the second one is optional and sets the severity level of the log. Events with a lower severity level than set one will not be logged.

 
error_log /var/log/nginx/error.log info;

These are the possible levels of severity (from lowest to highest) and their meaning:

  • debug: messages used for debugging.
  • info: informational messages.
  • notice: a notable event occurred.
  • warn: something unexpected happened.
  • error: something failed.
  • crit: critical conditions.
  • alert: errors that require immediate action.
  • emerg: the system is unusable.

Disabling the error log

The NGINX error log can be disabled by setting the error_log directive to off or by redirecting it to /dev/null:

 
error_log off;
error_log /dev/null;

Logging errors into multiple files

As is the case with access logs, you can log errors into multiple files, and you can use different severity levels too:

 
error_log /var/log/nginx/error.log info;
error_log /var/log/nginx/emerg_error.log emerg;

This configuration will log every event except those at the debug level event to the error.log file, while emergency events are placed in a separate emerg_error.log file.

Step 7 — Sending NGINX logs to Syslog

Apart from logging to a file, it's also possible to set up NGINX to transport its logs to the syslog service especially if you're already using it for other system logs. Logging to syslog is done by specifying the syslog: prefix to either the access_log or error_log directive:

 
error_log  syslog:server=unix:/var/log/nginx.sock debug;
access_log syslog:server=[127.0.0.1]:1234,facility=local7,tag=nginx,severity=info;

Log messages are sent to a server which can be specified in terms of a domain name, IPv4 or IPv6 address or a UNIX-domain socket path.

In the example above, error log messages are sent to a UNIX domain socket at the debug logging level, while the access log is written to a syslog server with an IPv4 address and port 1234. The facility= parameter specifies the type of program that is logging the message, the tag= parameter applies a custom tag to syslog messages, and the severity= parameter sets the severity level of the syslog entry for access log messages.

For more information on using Syslog to manage your logs, you can check out our tutorial on viewing and configuring system logs on Linux.

Step 8 — Centralizing your NGINX logs

In this section, we'll describe how you can centralize your NGINX logs in a log management service through Vector , a high-performance tool for building observability pipelines. This is a crucial step when administrating multiple servers so that you can monitor all your logs in one place (you can also centralize your logs with an Rsyslog server).

The following instructions assume that you've signed up for a free Logtail account and retrieved your source token. Go ahead and follow the relevant installation instructions for Vector  for your operating system. For example, on Ubuntu, you may run the following commands to install the Vector CLI:

 
curl -1sLf \ 'https://repositories.timber.io/public/vector/cfg/setup/bash.deb.sh' \ | sudo -E bash
 
sudo apt install vector

After Vector is installed, confirm that it is up and running through systemctl:

 
systemctl status vector

You should observe that it is active and running:

Output
● vector.service - Vector
     Loaded: loaded (/lib/systemd/system/vector.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2022-02-08 10:52:59 UTC; 48s ago
       Docs: https://vector.dev
    Process: 18586 ExecStartPre=/usr/bin/vector validate (code=exited, status=0/SUCCESS)
   Main PID: 18599 (vector)
      Tasks: 3 (limit: 2275)
     Memory: 6.8M
     CGroup: /system.slice/vector.service
             └─18599 /usr/bin/vector

Otherwise, go ahead and start it with the command below.

 
sudo systemctl start vector

Afterward, change into a root shell and append your Logtail vector configuration for NGINX into the /etc/vector/vector.toml file using the command below. Don't forget to replace the <your_logtail_source_token> placeholder below with your source token.

 
sudo -s
 
wget -O ->> /etc/vector/vector.toml \
    https://logtail.com/vector-toml/nginx/<your_logtail_source_token>

Then restart the vector service:

 
sudo systemctl restart vector

You will observe that your NGINX logs will start coming through in Logtail:

Logtail NGINX logs

Conclusion

In this tutorial, you learned about the different types of logs that the NGINX web server keeps, where you can find them, how to understand their formatting. We also discussed how to create your own custom log formats (including a structured JSON format), and how to log into multiple files at once. Finally, we demonstrated the process of sending your logs to Syslog or a log management service so that you can monitor them all in one place.

Thanks for reading, and happy logging!

Author's avatar
Article by
Ayooluwa Isaiah
Ayo is the Head of Content at Better Stack. His passion is simplifying and communicating complex technical ideas effectively. His work was featured on several esteemed publications including LWN.net, Digital Ocean, and CSS-Tricks. When he’s not writing or coding, he loves to travel, bike, and play tennis.
Got an article suggestion? Let us know
Next article
Monitoring Linux Authentication Logs: A Practical Guide
Learn to effectively monitor Linux authentication logs to detect security threats with this practical step-by-step guide
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Make your mark

Join the writer's program

Are you a developer and love writing and sharing your knowledge with the world? Join our guest writing program and get paid for writing amazing technical guides. We'll get them to the right readers that will appreciate them.

Write for us
Writer of the month
Marin Bezhanov
Marin is a software engineer and architect with a broad range of experience working...
Build on top of Better Stack

Write a script, app or project on top of Better Stack and share it with the world. Make a public repository and share it with us at our email.

community@betterstack.com

or submit a pull request and help us build better products for everyone.

See the full list of amazing projects on github