View Active Connections · Nuxt Skew Protection · Nuxt SEO

[NuxtSEO Pro](https://nuxtseo.com/ "Home")

- [Modules](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction)
- [Tools](https://nuxtseo.com/tools)
- [Pro](https://nuxtseo.com/pro)
- [Learn SEO](https://nuxtseo.com/learn-seo/nuxt) [Releases](https://nuxtseo.com/releases)

[Sign In](https://nuxtseo.com/auth/github)

[Nuxt SEO on GitHub](https://github.com/harlan-zw/nuxt-seo)

[User Guides](https://nuxtseo.com/docs/skew-protection/getting-started/introduction)

[API](https://nuxtseo.com/docs/skew-protection/api/use-skew-protection)

[Releases](https://nuxtseo.com/docs/skew-protection/releases/v1)

Skew Protection

- [Switch to Skew Protection](https://nuxtseo.com/docs/skew-protection/getting-started/introduction)
- [Switch to Nuxt SEO](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction)
- [Switch to Robots](https://nuxtseo.com/docs/robots/getting-started/introduction)
- [Switch to Sitemap](https://nuxtseo.com/docs/sitemap/getting-started/introduction)
- [Switch to OG Image](https://nuxtseo.com/docs/og-image/getting-started/introduction)
- [Switch to Schema.org](https://nuxtseo.com/docs/schema-org/getting-started/introduction)
- [Switch to Link Checker](https://nuxtseo.com/docs/link-checker/getting-started/introduction)
- [Switch to SEO Utils](https://nuxtseo.com/docs/seo-utils/getting-started/introduction)
- [Switch to Site Config](https://nuxtseo.com/docs/site-config/getting-started/introduction)
- [Switch to AI Ready](https://nuxtseo.com/docs/ai-ready/getting-started/introduction)

Search…```k`` /`

v1.1.1

- [Discord Support](https://discord.com/invite/275MBUBvgP)

### Getting Started

- [Introduction](https://nuxtseo.com/docs/skew-protection/getting-started/introduction)
- [Installation](https://nuxtseo.com/docs/skew-protection/getting-started/installation)

### Core Concepts

- [Check for Update Strategy](https://nuxtseo.com/docs/skew-protection/guides/update-strategies)
- [Update Notifications](https://nuxtseo.com/docs/skew-protection/guides/immediate-updates)
- [Performance](https://nuxtseo.com/docs/skew-protection/guides/performance)
- [Persistent Storage](https://nuxtseo.com/docs/skew-protection/guides/storage-configuration)
- [Notification UI](https://nuxtseo.com/docs/skew-protection/guides/ui-examples)
- [Cookie Consent](https://nuxtseo.com/docs/skew-protection/guides/cookie-consent)
- [View Active Connections](https://nuxtseo.com/docs/skew-protection/guides/live-connections)
- [Tracking User Pages](https://nuxtseo.com/docs/skew-protection/guides/route-tracking)

### Providers

- [External Providers](https://nuxtseo.com/docs/skew-protection/providers/external)
- [Cloudflare](https://nuxtseo.com/docs/skew-protection/providers/cloudflare)

Core Concepts

# View Active Connections

[Copy for LLMs](https://nuxtseo.com/docs/skew-protection/guides/live-connections.md)

Track how many users are connected to your application and which versions they're running. This is useful for admin dashboards, deployment monitoring, and understanding rollout progress.

Connection tracking only works with `sse` or `ws` update strategies. It does not support polling or external adapters (Pusher/Ably).

## [Setup](#setup)

Enable connection tracking in your Nuxt config using [`connectionTracking`](https://nuxtseo.com/docs/skew-protection/api/config#connectiontracking-boolean):

nuxt.config.ts

```
export default defineNuxtConfig({
  skewProtection: {
    connectionTracking: true,
    routeTracking: true, // optional: track which routes users are viewing
    ipTracking: true, // optional: track user IP addresses
  }
})
```

### [Limitations](#limitations)

- **Single instance only**: Stats are per-server process. With horizontal scaling, each instance only sees its own connections.
- **SSE/WS only**: Does not work with `polling` strategy or external adapters (Pusher/Ably).
- **Bot traffic excluded**: When you install `@nuxtjs/robots`, the module automatically excludes bot/crawler traffic from connection counts. See [Bot Traffic Filtering](https://nuxtseo.com/docs/skew-protection/guides/performance#bot-traffic-filtering).

### [IP Tracking](#ip-tracking)

IP tracking requires explicit opt-in via `ipTracking: true` due to privacy considerations. When enabled, the module extracts IP addresses from request headers in this order:

1. `cf-connecting-ip` (Cloudflare)
2. `x-forwarded-for` (proxies/load balancers)
3. `x-real-ip` (nginx)

The module only stores IPs in memory and exposes them via the stats API. Ensure your authorization hook properly restricts access to stats data.

## [Authorization](#authorization)

The server only sends stats to connections that pass authorization. Implement the `skew:authorize-stats` hook in a server plugin.

server/plugins/skew-auth.ts

```
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('skew:authorize-stats', async ({ event, authorize }) => {
    // Works with nuxt-auth-utils
    const session = await getUserSession(event)
    if (session?.user?.role === 'admin') {
      authorize()
    }
  })
})
```

### [Cloudflare with nuxt-auth-utils](#cloudflare-with-nuxt-auth-utils)

For [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) connections (e.g., with `cloudflare-durable` preset), the `event` object is not a full H3 event - it only contains `{ headers: Headers }`. This means `getUserSession()` won't work directly. You need to manually unseal the session cookie:

server/plugins/skew-auth.ts

```
import { unsealSession } from 'h3'

const ADMIN_EMAILS = ['admin@example.com']

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('skew:authorize-stats', async ({ event, authorize }) => {
    // Try getUserSession first (works for SSE/POST)
    const session = await getUserSession(event).catch(() => null)
    if (session?.user?.email && ADMIN_EMAILS.includes(session.user.email)) {
      authorize()
      return
    }

    // For WebSocket, manually unseal the session cookie
    if (event?.headers instanceof Headers) {
      const cookieHeader = event.headers.get('cookie') || ''
      const match = cookieHeader.match(/nuxt-session=([^;]+)/)
      if (match?.[1]) {
        const config = useRuntimeConfig()
        const password = config.session?.password || process.env.NUXT_SESSION_PASSWORD || ''
        if (password) {
          const unsealed = await unsealSession(null as any, { password }, decodeURIComponent(match[1])).catch(() => null)
          // Note: unsealSession returns { data: { user } }, not { user } directly
          const email = unsealed?.data?.user?.email
          if (email && ADMIN_EMAILS.includes(email)) {
            authorize()
          }
        }
      }
    }
  })
})
```

The key difference: `getUserSession()` returns `{ user: { email } }` while `unsealSession()` returns `{ data: { user: { email } } }`.

## [Using the Composable](#using-the-composable)

The [`useActiveConnections()`](https://nuxtseo.com/docs/skew-protection/api/use-active-connections) composable provides reactive access to connection statistics:

```
<script setup lang="ts">
import { useActiveConnections } from '#imports'

const { total, versions, authorized } = useActiveConnections()
</script>

<template>
  <div v-if="authorized">
    <p>Active users: {{ total }}</p>
    <ul>
      <li v-for="(count, version) in versions" :key="version">
        {{ version.slice(0, 8) }}: {{ count }} users
      </li>
    </ul>
  </div>
  <div v-else>
    Not authorized for stats
  </div>
</template>
```

### [Return Values](#return-values)

| Property | Type | Description |
| --- | --- | --- |
| `total` | `ComputedRef<number>` | Total active connections |
| `versions` | `ComputedRef<Record<string, number>>` | Map of buildId → connection count |
| `routes` | `ComputedRef<Record<string, number>>` | Map of route → connection count (requires `routeTracking`) |
| `connections` | `ComputedRef<ConnectionInfo[]>` | Individual connections with `id`, `version`, `route`, and `ip` (requires `ipTracking` for IP) |
| `yourId` | `ComputedRef<string \| undefined>` | Your connection ID to identify yourself in the list |
| `authorized` | `ComputedRef<boolean \| null>` | Whether the connection is authorized for stats (`null` while pending) |

## [Example: Admin Dashboard](#example-admin-dashboard)

```
<script setup lang="ts">
const { total, versions, authorized } = useActiveConnections()
const currentBuildId = useRuntimeConfig().app.buildId

const stats = computed(() => {
  const current = versions.value[currentBuildId] || 0
  const outdated = total.value - current
  return {
    total: total.value,
    current,
    outdated,
    percentUpToDate: total.value ? Math.round((current / total.value) * 100) : 100
  }
})
</script>

<template>
  <div v-if="authorized" class="grid grid-cols-4 gap-4">
    <div class="stat">
      <span class="label">Total Users</span>
      <span class="value">{{ stats.total }}</span>
    </div>
    <div class="stat">
      <span class="label">On Latest</span>
      <span class="value text-green">{{ stats.current }}</span>
    </div>
    <div class="stat">
      <span class="label">On Old Version</span>
      <span class="value text-orange">{{ stats.outdated }}</span>
    </div>
    <div class="stat">
      <span class="label">Rollout Progress</span>
      <span class="value">{{ stats.percentUpToDate }}%</span>
    </div>
  </div>
  <div v-else>
    Not authorized for live stats
  </div>
</template>
```

## [Custom Behavior with Hooks](#custom-behavior-with-hooks)

For advanced use cases, you can use hooks directly instead of the composable.

### [Client-Side: `skew:message`](#client-side-skewmessage)

Listen to all messages from the SSE/WebSocket connection:

plugins/custom-stats.client.ts

```
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hooks.hook('skew:message', (message) => {
    if (message.type === 'stats') {
      // Custom handling
      console.log('Connections:', message.total)
      console.log('Versions:', message.versions)
    }
  })
})
```

### [Server-Side: Connection Lifecycle](#server-side-connection-lifecycle)

Build custom tracking logic with Nitro hooks:

server/plugins/custom-tracking.ts

```
import { defineNitroPlugin } from 'nitropack/runtime'

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('skew:connection:open', ({ id, version, route, ip, send }) => {
    console.log(\`Client ${id} connected on ${version} from ${ip}\`)

    // Send custom welcome message
    send({ type: 'welcome', serverTime: Date.now() })
  })

  nitroApp.hooks.hook('skew:connection:close', ({ id }) => {
    console.log(\`Client ${id} disconnected\`)
  })
})
```

See [Nuxt Hooks](https://nuxtseo.com/docs/skew-protection/api/nuxt-hooks) and [Nitro Hooks](https://nuxtseo.com/docs/skew-protection/nitro-api/nitro-hooks) for full documentation.

[Edit this page](https://github.com/nuxt-seo-pro/nuxt-skew-protection/edit/main/docs/content/2.guides/6.live-connections.md)

[Markdown For LLMs](https://nuxtseo.com/docs/skew-protection/guides/live-connections.md)

Did this page help you?

[Cookie Consent Information about cookie consent requirements for using Nuxt Skew Protection.](https://nuxtseo.com/docs/skew-protection/guides/cookie-consent) [Tracking User Pages Monitor which pages users are viewing and invalidate specific routes.](https://nuxtseo.com/docs/skew-protection/guides/route-tracking)

On this page

- [Setup](#setup)
- [Authorization](#authorization)
- [Using the Composable](#using-the-composable)
- [Example: Admin Dashboard](#example-admin-dashboard)
- [Custom Behavior with Hooks](#custom-behavior-with-hooks)

[GitHub](https://github.com/harlan-zw/nuxt-seo) [ Discord](https://discord.com/invite/275MBUBvgP)

### [NuxtSEO](https://nuxtseo.com/ "Home")

- [Getting Started](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction)
- [MCP](https://nuxtseo.com/docs/nuxt-seo/guides/mcp)

Modules

- [Robots](https://nuxtseo.com/docs/robots/getting-started/introduction)
- [Sitemap](https://nuxtseo.com/docs/sitemap/getting-started/introduction)
- [OG Image](https://nuxtseo.com/docs/og-image/getting-started/introduction)
- [Schema.org](https://nuxtseo.com/docs/schema-org/getting-started/introduction)
- [Link Checker](https://nuxtseo.com/docs/link-checker/getting-started/introduction)
- [SEO Utils](https://nuxtseo.com/docs/seo-utils/getting-started/introduction)
- [Site Config](https://nuxtseo.com/docs/site-config/getting-started/introduction)
- [Skew Protection](https://nuxtseo.com/docs/skew-protection/getting-started/introduction)
- [AI Ready](https://nuxtseo.com/docs/ai-ready/getting-started/introduction)

### [NuxtSEO Pro](https://nuxtseo.com/pro "Nuxt SEO Pro")

- [Getting Started](https://nuxtseo.com/pro)
- [Dashboard](https://nuxtseo.com/pro/dashboard)
- [Pro MCP](https://nuxtseo.com/pro/docs/getting-started/mcp-setup)

### [Learn SEO](https://nuxtseo.com/learn-seo "Learn SEO")

Nuxt

- [Mastering Meta](https://nuxtseo.com/learn-seo/nuxt/mastering-meta)
- [Controlling Crawlers](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers)
- [Launch & Listen](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen)
- [Routes & Rendering](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering)
- [Staying Secure](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/security)

Vue

- [Vue SEO Guide](https://nuxtseo.com/learn-seo/vue)
- [Mastering Meta](https://nuxtseo.com/learn-seo/vue/mastering-meta)
- [Controlling Crawlers](https://nuxtseo.com/learn-seo/vue/controlling-crawlers)
- [SPA SEO](https://nuxtseo.com/learn-seo/vue/spa)
- [SSR Frameworks](https://nuxtseo.com/learn-seo/vue/ssr-frameworks)
- [SEO Checklist](https://nuxtseo.com/learn-seo/checklist)
- [Pre-Launch Warmup](https://nuxtseo.com/learn-seo/pre-launch-warmup)
- [Backlinks & Authority](https://nuxtseo.com/learn-seo/backlinks)

### [Tools](https://nuxtseo.com/tools "SEO Tools")

- [Social Share Debugger](https://nuxtseo.com/tools/social-share-debugger)
- [Robots.txt Generator](https://nuxtseo.com/tools/robots-txt-generator)
- [Meta Tag Checker](https://nuxtseo.com/tools/meta-tag-checker)
- [HTML to Markdown](https://nuxtseo.com/tools/html-to-markdown)
- [XML Sitemap Validator](https://nuxtseo.com/tools/xml-sitemap-validator)
- [Schema.org Validator](https://nuxtseo.com/tools/schema-validator)
- [Keyword Idea Generator](https://nuxtseo.com/tools/keyword-generator)
- [Keyword Research](https://nuxtseo.com/tools/keyword-research)
- [SERP Analyzer](https://nuxtseo.com/tools/serp-analyzer)
- [Domain Rankings](https://nuxtseo.com/tools/domain-rankings)

Copyright © 2023-2026 Harlan Wilton - [MIT License](https://github.com/harlan-zw/nuxt-seo/blob/main/license) · [mdream](https://mdream.dev)