When Apache sits behind a reverse proxy or load balancer, /var/log/apache2/access.log can end up blaming the same proxy IP for every request, which makes audits and troubleshooting unnecessarily mysterious. Logging the X-Forwarded-For header preserves the client IP chain that proxies pass along and keeps request records useful again.
Access logging is controlled by mod_log_config: LogFormat defines a named format string and CustomLog selects which format gets written to a log file. Request headers are available to LogFormat as %{Header-Name}i variables, so %{X-Forwarded-For}i can be added to each access log entry without changing the proxy or load balancer configuration.
The X-Forwarded-For header is not inherently trustworthy because any direct client can spoof it unless the edge proxy strips inbound values and rewrites it. The value may contain multiple comma-separated addresses (client first, then each proxy hop), so logging the raw header is best paired with also logging the socket IP for comparison. Changing the access log format can break downstream parsers and dashboards, so validate the configuration before reloading apache2 and update any log-ingest patterns as needed.
Steps to log X-Forwarded-For IP in Apache:
- Search /etc/apache2 for existing LogFormat and CustomLog directives.
$ sudo grep --recursive --line-number --extended-regexp '^[[:space:]]*(LogFormat|CustomLog)[[:space:]]' /etc/apache2 /etc/apache2/sites-available/000-default.conf:21: CustomLog ${APACHE_LOG_DIR}/access.log combined /etc/apache2/sites-available/default-ssl.conf:13: CustomLog ${APACHE_LOG_DIR}/access.log combined /etc/apache2/conf-available/other-vhosts-access-log.conf:2:CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log vhost_combined /etc/apache2/apache2.conf:212:LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined /etc/apache2/apache2.conf:213:LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined /etc/apache2/apache2.conf:214:LogFormat "%h %l %u %t \"%r\" %>s %O" common /etc/apache2/apache2.conf:215:LogFormat "%{Referer}i -> %U" referer /etc/apache2/apache2.conf:216:LogFormat "%{User-agent}i" agent ##### snipped ##### - Open the configuration file that contains the active logging directives.
$ sudo nano /etc/apache2/apache2.conf
On some Linux distributions the logging directives live under /etc/httpd/conf or in a vhost file under /etc/apache2/sites-enabled.
- Add a new LogFormat named combined_xff that logs X-Forwarded-For and the socket IP together.
LogFormat "%{X-Forwarded-For}i %a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined_xff
The first field is the raw X-Forwarded-For header (often a comma-separated chain), and %a logs the connected peer IP (often the reverse proxy).
- Change the CustomLog format name to use combined_xff for the access log.
CustomLog ${APACHE_LOG_DIR}/access.log combined_xffAltering the access log format can break log parsers, SIEM pipelines, and dashboards that expect the default combined layout.
- Save the updated configuration file.
- Test the Apache configuration syntax.
$ sudo apache2ctl -t Syntax OK
- Reload apache2 to apply the logging change.
$ sudo systemctl reload apache2
- Send a test request that includes an X-Forwarded-For value.
$ curl --silent --output /dev/null --write-out "%{http_code}\n" --header "X-Forwarded-For: 203.0.113.10" http://127.0.0.1/ 200 - Verify the access log shows the forwarded IP at the start of the line.
$ sudo tail --lines 1 /var/log/apache2/access.log 203.0.113.10 127.0.0.1 - - [10/Jan/2026:13:43:43 +0800] "GET / HTTP/1.1" 200 10671 "-" "curl/8.5.0"
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
