
# Website analytics

Website analytics is **off by default**. When enabled, the package records a lightweight, privacy-first page visit for each request: no cookies, no fingerprinting, no consent banner.

## Turn it on in production

Website analytics has no code to add: the `TrackPageVisit` middleware is auto-registered on the `web` middleware group when analytics is enabled. Turn it on in your **production** environment, not just a local `.env`:

```dotenv
RANETRACE_ENABLED=true
RANETRACE_WEBSITE_ANALYTICS_ENABLED=true
```

To try it during development, set the same flags in your local `.env`.

## Privacy model

Analytics is privacy-first by design: no cookies, no fingerprinting, no cross-site tracking. Visitors are identified only by salted, one-way HMAC hashes that never leave your data:

- `user_agent_hash`: the user agent, hashed.
- `session_id_hash`: a daily-rotating session identifier, hashed.

Raw identifiers are never stored, and the hashes are never shared across sites.

## What each visit captures

- Path, referrer, and UTM parameters (source, medium, campaign, term, content).
- Device type (mobile, tablet, desktop) and browser.
- A human-probability score and country code.

## Keeping bots out of your numbers

Bot traffic is filtered server-side, before a visit is ever recorded: CrawlerDetect, extra bot patterns, a client-hints consistency check that catches a request claiming to be Chrome while its headers say otherwise, and a minimum human-probability score (default 70). That runs on every request and needs nothing added to your pages.

For the automation those signals believe, there is an optional beacon:

```dotenv
RANETRACE_WEBSITE_ANALYTICS_BEACON_ENABLED=true
```

The beacon rides the `@ranetraceErrorTracking` directive you already have in your layout, and sends back one opaque token per visit, so only the visits a real, visible browser actually rendered are counted. Nothing about the visitor is collected or stored: the token says a browser rendered the page and nothing else.

Two things to know before you turn it on. It writes through the queue, so the beacon needs a real queue driver rather than `sync`. And it must stay off on pages served from a full-page cache, where every visitor would receive the same token.

Whether you run the beacon or not, Ranetrace's traffic pages say how many visits were kept out, so a quiet day and a filtered day never look the same.

## Configuration

From `config/ranetrace.php`:

```dotenv
RANETRACE_WEBSITE_ANALYTICS_ENABLED=true
RANETRACE_WEBSITE_ANALYTICS_THROTTLE_SECONDS=30
```

### Excluded paths

Publish the config to skip paths you do not want tracked. Excluded paths match the first URL segment, so adding `admin` excludes every `/admin/*` route:

```php
// config/ranetrace.php
'website_analytics' => [
    'excluded_paths' => [
        'horizon', 'nova', 'telescope', 'admin', 'filament',
        'api', 'debugbar', 'storage', 'livewire',
        // add your own paths
    ],
],
```

### Custom request filter

For filtering logic beyond a path list, implement the `RequestFilter` contract and register it in the published config:

```php
use Illuminate\Http\Request;
use Ranetrace\Laravel\Analytics\Contracts\RequestFilter;

class InternalTrafficFilter implements RequestFilter
{
    public function shouldSkip(Request $request): bool
    {
        return (bool) $request->header('X-Internal-Client');
    }
}
```

```php
// config/ranetrace.php
'request_filter' => \App\Analytics\InternalTrafficFilter::class,
```

## Testing your setup

```bash
php artisan ranetrace:test-analytics
```

## Disabling

```dotenv
RANETRACE_WEBSITE_ANALYTICS_ENABLED=false
```

With the feature off, the middleware records nothing.
