The recommended LogFormat

Plain combined works. These extra headers are worth 145 points of the ruleset for about 40 bytes a line.

Loghound works out of the box on plain combined. Detection is materially better if you log a few more headers, and the detection quality ceiling is set by exactly this.

01 · Apache

Add the format to /etc/apache2/apache2.conf (or /etc/httpd/conf/httpd.conf) and reference the nickname from your vhost’s CustomLog:

LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O %D \"%{Referer}i\" \"%{User-Agent}i\" \
\"%{Accept}i\" \"%{Accept-Language}i\" \"%{Accept-Encoding}i\" \
\"%{Sec-CH-UA}i\" \"%{Sec-CH-UA-Platform}i\" \"%{Sec-CH-UA-Mobile}i\" \
\"%{Sec-Fetch-Site}i\" \"%{Sec-Fetch-Mode}i\" \"%{Sec-Fetch-Dest}i\" \"%{Sec-Fetch-User}i\" \
\"%{X-Forwarded-For}i\" \"%H\" \"%{SSL_PROTOCOL}x\" \"%{SSL_CIPHER}x\"" loghound
CustomLog ${APACHE_LOG_DIR}/example_com_access.log loghound

Then reload and re-run the wizard so it picks up the new format:

apache2ctl configtest && systemctl reload apache2
sudo -u loghound php /opt/loghound/bin/loghound-setup
Three ways this silently does nothing, in the order people hit them

1. Do not reuse the name combined. Debian and Ubuntu already define that nickname in apache2.conf, and redefining it inside a virtual host does not reliably win. You edit the file, you reload, the config is correct, and the lines keep coming out in the old shape with nothing in any error log to tell you why. Give your format its own name, as above. If you only want the duration, combined_d is a fine name for it:

LogFormat "%h %l %u %t \"%r\" %>s %O %D \"%{Referer}i\" \"%{User-Agent}i\"" combined_d
CustomLog ${APACHE_LOG_DIR}/example_com_access.log combined_d

2. Reload, and check that the reload really happened. A graceful reload keeps the same master process, so the process start time does not move and the server looks untouched. Compare the modification time of the vhost against the last AH00493: SIGUSR1 received. Doing graceful restart in error.log. A config edited hours ago on a server never reloaded is the most common version of this.

3. Tell Loghound the shape changed. It stores the format per source, so the moment a line gains a field the stored format does not describe, every new line becomes a parse error. The lines are still being written and still being read, and the counter climbs while nothing appears in the panel. Re-run bin/loghound-setup, or rescan the source in Settings › Log sources, and check bin/loghound-tail --status --human afterwards. Parse errors should be back to zero within a minute.

The quickest way to see which of the three you are in: look at one real line. If it has no duration, you are in 1 or 2. If it has one and the panel is still empty, you are in 3.

Be honest with yourself about the trade

These lines are roughly two to three times longer than combined. A combined line in the Loghound fixtures averages about 320 bytes; the recommended format lands near 800. On a site doing ten million requests a day that is real disk. On most sites it is not. Decide with du, not with a feeling.

02 · nginx

The equivalent, in the http {} block:

log_format loghound '$host:$server_port $remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent $request_time '
                    '"$http_referer" "$http_user_agent" '
                    '"$http_accept" "$http_accept_language" "$http_accept_encoding" '
                    '"$http_sec_ch_ua" "$http_sec_ch_ua_platform" "$http_sec_ch_ua_mobile" '
                    '"$http_sec_fetch_site" "$http_sec_fetch_mode" '
                    '"$http_sec_fetch_dest" "$http_sec_fetch_user" '
                    '"$http_x_forwarded_for" "$server_protocol" '
                    '"$ssl_protocol" "$ssl_cipher"';

access_log /var/log/nginx/example_com_access.log loghound;
nginx -t && systemctl reload nginx
sudo -u loghound php /opt/loghound/bin/loghound-setup
03 · What each header buys you

You do not have to take all of it. Every header is independent.

Log thisWhat becomes possibleRule
%v:%pPer-vhost separation. Without it, every site sharing a log file is one dataset.
%DThe whole Performance view. Also lets you see a scraper hammering your most expensive endpoint.
%OBytes actually sent, rather than the response-body size. More accurate bandwidth attribution per bot.
AcceptSharpens the fingerprint considerably: these strings vary a lot between real browser versions and are almost always wrong or absent in scripted clients.strengthens the fleet rule
Accept-LanguageFingerprint, plus the visitor hash. A whole fleet sharing one language header while claiming to be spread across a dozen countries is a strong tell.fleet, timezone
Accept-EncodingFingerprint. Scripted clients routinely send a short or unusual list.strengthens the fleet rule
Sec-CH-UAThe single most valuable addition. A Chrome claim can be checked against the client hint Chrome itself sends, and spoofers change the User-Agent and forget this one constantly.75 points
Sec-CH-UA-PlatformCross-check the claimed operating system. Windows in the User-Agent and Linux in the hint is a headless container in a costume.70 points
Sec-CH-UA-MobileThe same cross-check for the mobile claim.platform mismatch
Sec-Fetch-SiteTells a real navigation from a sub-resource fetch and from a cross-site request. Catches direct asset fetching that never loaded the page.strengthens no sub-resources
Sec-Fetch-ModeA “browser” whose HTML fetch is not a navigation is not browsing.fingerprint
Sec-Fetch-DestLets asset classification come from the browser rather than from guessing at file extensions. By far the most reliable way to tell a document navigation from a sub-resource.improves the asset ratio
Sec-Fetch-UserPresent only when a navigation was triggered by real user activation, so its absence on a top-level HTML request is meaningful.contributes to no interaction
X-Forwarded-ForEssential behind a proxy or load balancer. Without it every visitor appears to come from the proxy and address clustering and geography become meaningless. Configure trusted_proxies too.all address-based rules
%HProtocol version. Part of the fingerprint, and a client claiming a modern browser while speaking HTTP/1.1 to an HTTP/2-capable server is worth a look.fingerprint
SSL_PROTOCOL SSL_CIPHERA coarse TLS fingerprint. The cipher a client offers still separates browser families from Go, curl and Python clients.fingerprint

If you only add two things

Add Sec-CH-UA and Sec-CH-UA-Platform. They cost about forty bytes a line and they are worth 145 points of the ruleset.

04 · What plain combined still gives you
PLAIN COMBINEDfingerprint: 2 componentsUser-Agent and protocol onlyheader consistency rules:cannot fire at allabout 320 bytes a lineRECOMMENDED FORMATfingerprint: 11 componentshighly discriminatingtwo client hints alone unlock145 points of the rulesetabout 800 bytes a line

Figure 1 — the trade, measured. Two to three times the disk per line, for a fingerprint that goes from two components to eleven and two rules that go from silent to decisive. Decide with du, not with a feeling.

Address, timestamp, method, path, protocol, status, bytes, referrer and User-Agent. From those alone Loghound still does everything on the behavioural plane — asset ratio, session shape, inter-request timing, request-rate periodicity — and, with the beacon, the entire execution plane.

What you lose is the header-consistency family of rules. The Sec-CH-UA and platform mismatch rules cannot fire at all, and they are two of the cheapest and most reliable signals in the whole ruleset. The fingerprint also collapses to User-Agent plus protocol, which makes the cluster signal noisier in both directions.

JA4 and JA3

The schema defines a JA4 field and nothing populates it. Apache and nginx cannot produce one; it comes from a TLS-terminating proxy such as HAProxy. If you have one, log it into a header and map it — the field is waiting. Nothing in this version depends on it.

Loghound is open source and MIT licensed. Questions about the Opensolr half — the account, the indexes, the plan — go to opensolr.com/contact; questions about the software itself belong on GitHub.

Loghound Documentation