---
title: Userdir traffic stats (tilde.club)
category: software
Author: deepend
---

So you want traffic stats for your `~/public_html` site, This stats system generates a **sanitized** report per user from the server’s nginx logs, then lets you publish it *only if you choose to*.

## What this gives you

* Daily totals: requests, pageviews, unique visitors (hashed per-day), bytes
* “Top pages” list (query strings removed)
* Referrers by **domain only** (no full URLs)
* Browser/user-agent buckets (Chrome/Firefox/Bot/etc.)
* HTTP status code counts
* Optionally: a nice HTML dashboard generated by `goaccess`

No raw IPs are shown in the report.

## Opt-in (required)

Nothing is generated for you unless you opt in.

Create the opt-in flag:

```sh
mkdir -p ~/.config/tilde-stats
touch ~/.config/tilde-stats/enabled
```

That’s it. The nightly job will include you from then on.

## Where the files are written

Reports are generated into:

* `~/.local/share/tilde-stats/index.html`
* `~/.local/share/tilde-stats/stats.json`

These files are **not automatically public**.

## Publishing the report on your website

There are two ways to publish the report:

* **Copy (recommended)**: keeps `~/.local` private and uses normal web permissions.
* **Symlink (works, but requires permissions)**: points your website at `~/.local/...`, which means nginx must be able to traverse those dot-directories.

Your stats page will be:

* `https://tilde.club/~YOURNAME/stats/`

### Recommended: copy into `~/public_html/stats/`

```sh
mkdir -p ~/public_html/stats
cp -f ~/.local/share/tilde-stats/index.html ~/public_html/stats/index.html
cp -f ~/.local/share/tilde-stats/stats.json  ~/public_html/stats/stats.json
chmod 644 ~/public_html/stats/index.html ~/public_html/stats/stats.json
```

### Alternative: symlink into `~/public_html/stats/` (requires permissions)

```sh
mkdir -p ~/public_html/stats
ln -sf ~/.local/share/tilde-stats/index.html ~/public_html/stats/index.html
ln -sf ~/.local/share/tilde-stats/stats.json  ~/public_html/stats/stats.json
```

If you use symlinks, nginx must be able to **traverse** the real path under `~/.local/...` (see permissions below).

## Permissions (important, because Linux will happily sabotage you)

For nginx to serve a file, it must be able to:

* **traverse** every directory in the path (execute bit, `+x`)
* **read** the file (`+r`)

### Minimum permissions for userdir websites

If your `~/public_html` site already works, you probably already have this, but here’s the standard expectation:

```sh
chmod 711 ~
```

`chmod 711 ~` lets the web server traverse your home directory without listing it.

### If you are COPYING files (recommended)

Make sure your published files are world-readable:

```sh
chmod 644 ~/public_html/stats/index.html ~/public_html/stats/stats.json
```

Your `~/.local/share/tilde-stats/` directory can stay private.

### If you are SYMLINKING to `~/.local/share/tilde-stats/` (current reality)

Because the report lives in a dot-directory path, nginx must be able to traverse:

* `~`
* `~/.local`
* `~/.local/share`
* `~/.local/share/tilde-stats`

And it must be able to read the report files themselves.

Use these permissions:

```sh
chmod 711 ~/.local ~/.local/share ~/.local/share/tilde-stats
chmod 644 ~/.local/share/tilde-stats/index.html ~/.local/share/tilde-stats/stats.json
```

Notes:

* `711` is better than `755` for dot-directories since it allows traversal but not directory listing.
* If the generator writes the files with private permissions, you may need to re-apply the `chmod 644` on the two report files after regeneration if you are serving them directly from `~/.local`.

## Updating / regenerating

Stats are generated by an admin-run job (typically nightly). You don’t need to run anything manually.

If you publish via **copy**, re-run the copy commands after the nightly update to refresh the public version.

If you publish via **symlink**, the page updates automatically (as long as permissions allow nginx to read it).

## Disabling / opting out

Remove the opt-in flag:

```sh
rm -f ~/.config/tilde-stats/enabled
```

No new reports will be generated for you after that.

(Any old published copies or symlinks you created are your responsibility to delete.)

## Troubleshooting

### “My stats page 403s / 404s”

* Confirm the public files exist:

```sh
ls -l ~/public_html/stats/
```

* Confirm permissions all the way down the path:

```sh
namei -l ~/public_html/stats/index.html
```

If you’re using symlinks into `~/.local`, also check the target path:

```sh
namei -l ~/.local/share/tilde-stats/index.html
```

Every directory in that output should have `x` for others (traversable), and the file should have `r` for others.

### “The report exists in ~/.local/share but isn’t public”

That’s expected. It’s private until you copy or link it into `~/public_html`.

### “My report is empty”

Either:

* No traffic in the selected window, or
* You opted in recently and the next scheduled run hasn’t happened yet.

(Servers do not read minds, only logs.)
