Apache LogFormat: Capturing Actual Requested Hostname for Wildcard VirtualHost (*.domain.com)


9 views

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:

  1. Restart Apache: sudo apache2ctl graceful
  2. Make test requests to various subdomains
  3. 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