# Nginx log analytics *from the log nginx already writes*

> The page https://skaldanalytics.com/server-log-analytics/nginx/ as Markdown. Every page and guide: https://skaldanalytics.com/llms.txt . Skald is source-available (Elastic License 2.0).

set-up · nginx · with or without Docker

Nginx log analytics with no change to nginx for most sites. One command on your server finds the site's access log in nginx's own settings and sends each new line to your Skald, with or without Docker. Skald sorts every line into people, AI assistants, search engines, bots and your own traffic.

*Nginx log analytics* is reading the access log nginx writes for every request it answers, and turning it into visits, sources and pages. Skald reads nginx's default combined format, and its own extended format with the host, the time taken and the country.

[Start your 30-day free trial](https://skaldanalytics.com/beta/?choice=trial#signup) [Open the live demo](https://spcaeo.github.io/skald/)

30-day free trial · no card · we set it up within one working day · free to self-host

- Most sites change nothing in nginx's…
- Skald reads the first lines and lets…
- What the one command leaves on your…
- One check lights a lamp for each…
- Skald on the same nginx server,…

nginx's settings · sample · searched for example.com

1. Reads nginx.conf, conf.d/ and sites-enabled/
2. shop.example.org is not your site: skipped
3. server_name example.com: this block answers for your site
4. status.example.net: skipped
5. `$host` tried as example.com and www.example.com; one file is there

found in the web server's settings: `/var/log/nginx/example.com.access.log`

Sample settings. The one command reads them; it never edits them. A block with no access_log of its own writes to the http block's log.

[01] the log format

## Most sites change nothing in nginx's log format. *A few fields add something.*

nginx's default format, called combined, already holds the six facts Skald needs: address, time, request, status, referrer and user agent. A field snapped on at the end of the line adds one more answer. Pick a field to see the line it writes and what it gives you.

snap one on

log_format combined '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"' nothing: nginx writes combined when access_log names no format

the line nginx writes · sample

198.51.100.7 - - [23/Oct/2026:09:14:03 +0000] "GET / HTTP/2.0" 200 8312 "https://www.google.com/" "Mozilla/5.0 (Windows NT 10.0) Chrome/130.0"

What it gives

Read as it is. Visits, sources, pages, AI crawlers and bots all come from these six facts.

Who needs it

Every site

Read by Skald as

`nginx_combined`

Sample lines. A field goes inside the log_format quotes, at the very end. Then `sudo nginx -t && sudo systemctl reload nginx` tests the settings and reloads them. Old lines stay as they were; only new lines carry the field.

[AI traffic in the same log](https://skaldanalytics.com/ai-traffic-analytics/) [Next.js analytics](https://skaldanalytics.com/integrations/nextjs/) [IP block list from your logs](https://skaldanalytics.com/security/block-list/)

[02] which format is it?

## Skald reads the first lines and lets every format try them

You do not tell Skald which format your log is in. It tries every format it knows on the first 200 lines, keeps the one that reads the most, and the richest wins a tie. A later line the winner cannot read is tried against the others. Pick a log.

pick a log

formatlines it reads, of 10

1. `nginx_extended`
2. `nginx_combined`
3. `caddy_json`
4. `six CDN and drain formats`

detected `nginx_combined` · 10 of 10 lines read · day **published**

nginx's default. Nothing else reads these lines, so combined wins with all ten.

Scores from Skald's own detect step, run on ten sample lines of each log. The limit of unreadable lines is set per install; the default is 1%.

[03] one command, two ways to run

## What the one command leaves on your nginx server

The command is the same on every server. It uses Docker when Docker works there, or else a systemd service, the background-service manager most Linux servers have, which needs Python 3.9 or newer. A server set up one way before keeps that way. Flip the switch and open the drawer.

flip it

/ what stays on the server

- `skald-ship`a container, 128 MB of memory at most, user 10001; the log's folder is mounted read-only; Docker restarts it after a reboot
- `/srv/skald/ship-token`root:adm, 0640: used, never printed, and it can only add lines to this one site
- `/srv/skald/ship-state/`its place in the log
- `/srv/skald/ship.conf`the log file and the send address, no token
- `/usr/local/bin/skald-ship-check`checks, changes nothing
- `/usr/local/bin/skald-ship-uninstall`removes only what the command made

- **Never edits nginx.** It reads nginx's settings and the log, nothing else.
- **New lines only.** It starts at the end of the log; older lines are not sent.
- **Never lost.** It saves its place only after Skald has taken a batch, and sends again after a failure, waiting 1 second, then twice as long each time, up to 5 minutes.
- **Never twice.** Batches are numbered, and Skald drops one it already has.
- **Log rotation.** logrotate is the tool that renames or empties the log each day and compresses old ones. The shipper follows both kinds of rotation. Keep `delaycompress`, so the last rotated file is not compressed while it may still be read.
- **Small sends.** Gzip batches of at most 2,000 lines or 4 MiB, over HTTPS.

Options go after `sudo bash -s --`: `--log <path>` names the file, `--yes` asks nothing, `--dry-run` shows every step and changes nothing.

[one-command log set-up](https://skaldanalytics.com/docs/setup/one-command/) [how server log analytics works](https://skaldanalytics.com/server-log-analytics/how-it-works/)

[04] check it

## One check lights a lamp for each thing that can go wrong

`sudo skald-ship-check` reads only and changes nothing. Each line says OK, or PROBLEM with the fix under it, and the end holds a block to send us, with no token and no address in it. Pick a fault.

pick a fault

✓ OK www.example.com writes to /var/log/nginx/example.com.access.log

fix All OK.

exit 0

Sample output, one line of the check at a time. The words are the check's own. A Next.js site whose log lacks the prefetch field gets an ADVICE line, which is not a problem and does not change the exit.

[05] other set-ups

## Skald on the same nginx server, behind a CDN, and the limits

The one command fits most servers. Three set-ups change the plan, and a few things stay out of reach whatever you do.

1. **Skald on the same server**

No shipper at all. Run Skald in Docker and mount the log folder read-only, for example `-v /var/log/nginx:/logs:ro`. Skald reads the log in place.

[self-hosted analytics with Docker](https://skaldanalytics.com/self-host/docker/)

2. **Behind Cloudflare**

A page Cloudflare serves from its cache never reaches nginx. Add the Skald Worker, a small script Cloudflare runs in front of your site: Skald keeps the Worker's cache hits and takes the rest from nginx.

[Cloudflare log analytics](https://skaldanalytics.com/server-log-analytics/cloudflare/)

3. **One log per site**

Each site needs its own `access_log` in its own server block. A log shared by several host names mixes their visitors, and the check says so.

[add a site to Skald](https://skaldanalytics.com/docs/setup/add-a-site/)

### What it does not do

- It does not edit nginx. The fields above are yours to add; the check only says when the Next.js one is missing.
- It sees only requests that reach nginx. Behind a CDN, cached pages need the CDN's own log or the Skald Worker.
- A plain combined line has no country. Skald looks the address up in a GeoIP country file (a list of which addresses are in which country) when one is set on the Skald server; without one, the country says Not recorded.
- It sends only lines written after the install. Older lines in the file are not sent.

![Skald's Overview with sample data: visitors now, pages read, leads, visitors from AI, a map of visitors and the newest visits](https://skaldanalytics.com/img/overview-today-800.webp)

The Overview, with sample data, read from an nginx access log.

[Keep your tool. Add Skald beside it, 30 days free](https://skaldanalytics.com/beta/?choice=trial#signup) [Open the live demo](https://spcaeo.github.io/skald/)

keep reading

## More on nginx log analytics

- [How it works](https://skaldanalytics.com/server-log-analytics/how-it-works/): One log line, six facts, checked names, a render test, and every figure counted twice.
- [One-command log set-up](https://skaldanalytics.com/docs/setup/one-command/): One line on your nginx or Apache server, with or without Docker.
- [Skald pricing](https://skaldanalytics.com/pricing/): Per site, a 30-day free trial with no card, or free to self-host.

questions

## Nginx log analytics FAQ

Straight answers, including what Skald does not do.

[Read the docs](https://skaldanalytics.com/docs/) [Ask us a question](https://skaldanalytics.com/contact/)

**Do I have to change my nginx log format?**

No, for most sites. Skald reads nginx's default combined format as it is. A Next.js site needs one field added at the end of the log_format, so a click inside the site counts as a page view. Skald's extended format, with the host, the time taken and Cloudflare's country, is optional.

**Does it need Docker?**

No. The one command uses Docker when Docker works on the server. Otherwise it installs the shipper as a systemd service, which needs Python 3.9 or newer. If neither is there, it stops and says what to install.

**Will I lose lines when nginx's log rotates?**

No. The shipper follows logrotate's renames and copytruncate, and it saves its place only after Skald has taken a batch. Keep delaycompress in the logrotate settings, so the last rotated file is not compressed while the shipper may still be reading it.

**Can a line be counted twice if the network fails?**

No. Batches are numbered and Skald drops a batch it already has. A batch that did not reach Skald is sent again, so a line is neither lost nor counted twice.

**Several sites share one access log. Is that a problem?**

Yes. Each site needs its own access_log line in its own server block, or the lines of the other sites are mixed in. sudo skald-ship-check says when the log is shared with other host names.

**Where do the country figures come from?**

From the log when it carries one: Skald's extended format records Cloudflare's country header as cc=. Otherwise from a GeoIP country file on the Skald server, when one is set. With neither, the country says Not recorded. It is never guessed.
