When using a wildcard VirtualHost configuration like *.example.com
in Apache 2.2, the server automatically handles all subdomains with the same site configuration. While convenient, this creates a logging challenge where traditional log formats only record the wildcard pattern rather than the actual requested hostname.
The standard vhost_combined
format uses the %v
variable which expands to the virtual host's ServerName
. In our case, this always shows as *.example.com
rather than the specific subdomain that was requested (e.g., blog.example.com
or api.example.com
).
The key is to use the %{Host}i
directive which captures the exact Host
header from the HTTP request. Here's how to implement it:
LogFormat "%{Host}i %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" custom_host_log
CustomLog /var/log/apache2/access.log custom_host_log
For more comprehensive logging, you might want to combine both the virtual host and actual host:
LogFormat "%v %{Host}i %h %l %u %t \"%r\" %>s %b" combined_with_host
Or include the port if needed:
LogFormat "%{Host}i:%{remote}p %h %l %u %t \"%r\" %>s %b" host_with_port
After implementing the new log format, make sure to:
- Restart Apache:
sudo apache2ctl graceful
- Make test requests to various subdomains
- Verify the log entries show the correct hostnames
Be aware that:
- Some clients might send malformed Host headers
- Proxy setups might modify the Host header
- IPv6 addresses might appear differently in logs
For these cases, consider adding conditional logging:
LogFormat "%{Host}i|%{X-Forwarded-Host}i %h %l %u %t \"%r\" %>s %b" proxy_aware_host
When using wildcard VirtualHost configurations like *.example.com
in Apache 2.2, the standard logging variables will only show the wildcard pattern rather than the actual requested subdomain. This makes debugging and analytics significantly harder.
The common variables like %v
(canonical ServerName) and %V
(ServerName according to UseCanonicalName) both return the wildcard pattern:
LogFormat "%v %h %l %u %t \"%r\" %>s %b" common
# Output: *.example.com 192.168.1.1 - - [10/Oct/2023:15:32:45 +0000] "GET / HTTP/1.1" 200 1234
The %{Host}i
variable captures the exact Host header from the HTTP request:
LogFormat "%{Host}i %h %l %u %t \"%r\" %>s %b" custom_with_host
# Output: sub.example.com 192.168.1.1 - - [10/Oct/2023:15:32:45 +0000] "GET / HTTP/1.1" 200 1234
For comprehensive wildcard vhost logging, combine multiple variables:
LogFormat "%{Host}i %v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" wildcard_vhost
In your VirtualHost configuration:
<VirtualHost *:80>
ServerName *.example.com
ServerAlias example.com
DocumentRoot /var/www/example
CustomLog /var/log/apache2/example-access.log wildcard_vhost
ErrorLog /var/log/apache2/example-error.log
# Rest of your configuration...
</VirtualHost>
For security purposes, you might want to verify the Host header matches your domain pattern:
SetEnvIf Host ^([^.]+\.)?example\.com$ valid_host
CustomLog /var/log/apache2/example-access.log wildcard_vhost env=valid_host
- HTTP/1.0 requests may not include Host header
- Malicious requests might spoof Host headers
- Some CDN configurations may modify Host headers