When configuring Nginx server blocks, many administrators want to create separate log files for each virtual host. The natural approach is to use the $server_name
variable in the log file path:
access_log /var/log/nginx/$server_name.access.log;
error_log /var/log/nginx/$server_name.error.log;
However, this often doesn't work as expected, resulting in literal filenames containing "$server_name" instead of the actual domain name.
Nginx processes log file paths differently than other directives. The $server_name
variable isn't expanded in log file paths because:
- Log file paths are evaluated very early in the request processing phase
- The
$server_name
variable isn't available at the time log paths are resolved - Nginx needs to know the log file location before processing the request
Method 1: Separate Server Blocks
The most reliable approach is to create separate server blocks for each domain:
server {
listen 80;
server_name domain1.com;
access_log /var/log/nginx/domain1.com.access.log;
error_log /var/log/nginx/domain1.com.error.log;
# ... other configurations ...
}
server {
listen 80;
server_name domain2.com;
access_log /var/log/nginx/domain2.com.access.log;
error_log /var/log/nginx/domain2.com.error.log;
# ... other configurations ...
}
Method 2: Using map Directive
For dynamic log file naming, you can use the map
directive:
http {
map $host $logname {
default 'default';
domain1.com 'domain1';
domain2.com 'domain2';
# Add more mappings as needed
}
server {
listen 80;
server_name _;
access_log /var/log/nginx/$logname.access.log;
error_log /var/log/nginx/$logname.error.log;
# ... other configurations ...
}
}
Method 3: Log Rotation Script
Another approach is to use a single log file and separate them later with a script:
# In nginx.conf
access_log /var/log/nginx/combined.log;
error_log /var/log/nginx/error.log;
# In a separate script (e.g., /usr/local/bin/nginx_logrotate.sh)
#!/bin/bash
awk '{print > "/var/log/nginx/" $1 ".access.log"}' /var/log/nginx/combined.log
: > /var/log/nginx/combined.log
- Always create log directories in advance when using per-domain logging
- Consider using logrotate for managing log files
- For high-traffic sites, evaluate the performance impact of multiple log files
- Document your logging strategy for maintenance purposes
If you're comfortable compiling Nginx from source, you can modify the core to support dynamic log filenames. However, this requires:
- Deep understanding of Nginx internals
- Willingness to maintain custom patches
- Acceptance of potential stability issues
When configuring multiple domains on an Nginx server, you might want to separate logs by domain name for better organization. The logical approach would be to use the $server_name
variable in your log file paths, like this:
access_log /var/log/nginx/$server_name.access.log;
error_log /var/log/nginx/$server_name.error.log;
However, as many administrators discover, this doesn't work as expected. Instead of creating domain-specific log files (e.g., example.com.access.log
), Nginx literally creates files named $server_name.access.log
.
In Nginx versions prior to 1.7.1, variables in the access_log path weren't supported in the main configuration. This is because:
- Log files are opened when Nginx starts, before it processes any requests
- The
$server_name
variable isn't available during the configuration phase - Nginx needs the log file to exist when it starts
Here are several approaches to achieve domain-specific logging:
Option 1: Separate Server Blocks
The most straightforward solution is to create dedicated server blocks for each domain:
server {
listen 80;
server_name example.com;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
# ... other configuration ...
}
server {
listen 80;
server_name another.com;
access_log /var/log/nginx/another.com.access.log;
error_log /var/log/nginx/another.com.error.log;
# ... other configuration ...
}
Option 2: Using Nginx 1.7.1+ with Conditional Logging
If you're using Nginx 1.7.1 or later, you can use conditional logging with variables:
map $host $logname {
default 'unknown';
~^(www\.)?(?.+)$ $domain;
}
access_log /var/log/nginx/$logname.access.log;
Option 3: Log Rotation with Post-Processing
For older Nginx versions, you can use a single log file and split it later:
access_log /var/log/nginx/combined.log;
Then use a log rotation script like this:
#!/bin/bash
# Split logs by domain
cd /var/log/nginx
for domain in $(awk '{print $4}' combined.log | sort -u); do
grep " $domain " combined.log > ${domain}.access.log
done
> combined.log
If you want to create log directories dynamically, you'll need a startup script:
#!/bin/bash
# Create log directories for all configured server names
for conf in /etc/nginx/sites-enabled/*; do
for server in $(grep -Po 'server_name \K[^;]+' $conf); do
mkdir -p "/var/log/nginx/${server}"
done
done
- Always test configuration changes with
nginx -t
- Consider log rotation to prevent disk space issues
- For high-traffic sites, evaluate the performance impact of multiple log files
- Ensure proper permissions on log directories (typically nginx user needs write access)
Remember that while newer Nginx versions support more flexible logging configurations, the separate server block approach remains the most reliable solution across all versions.