Skip to main content
Documentation

Laravel Installation Guide

Last researched 2026-08-26

This is the canonical Virent installation guide for Laravel applications.

Install with your coding agent

Hand the install to Claude Code, Codex, Cursor or another coding agent. It reads this guide, asks for your keys and keeps the server secret out of client code.

Prompt
Install Virent analytics on my Laravel website.

Read the canonical guide first and follow its code exactly:
https://virent.app/docs/installation/laravel.md

Install two separate things:
1. Human Analytics (browser): Install the browser script in the shared Blade layout used by public pages.
2. AI Crawler Analytics (server): Create a Laravel middleware class and append it to the web middleware group.

Values I will give you (they are in Virent under Settings > Connection):
- Publishable browser key, starts with vha_pk_. Safe in browser code.
- Site ID (VIRENT_SITE_ID) and server secret (VIRENT_INGEST_SECRET, starts with vha_sk_). Server only.
Ask me for them. Never invent a key or use a masked preview such as vha_sk_••••.

Human Analytics best practices:
- Add the script once to the shared Blade layout, usually resources/views/layouts/app.blade.php.
- Use the publishable key in browser markup only.
- Keep the script out of admin-only or partial templates unless those are the only public pages.

AI Crawler Analytics best practices:
- Add Virent to Laravel's web middleware group so normal web page requests are covered.
- Use the Http facade with server-only .env values for crawler telemetry.
- Use terminable middleware so telemetry is sent after the application response and includes the final status code.

Rules:
- Keep VIRENT_INGEST_SECRET in server environment variables. Never put it in client code, public env variables (NEXT_PUBLIC_, VITE_, PUBLIC_) or committed files.
- Exclude API routes, static assets, images, favicons, robots.txt and sitemaps from crawler capture.
- If the site sends a Content-Security-Policy, allow https://app.virent.app in script-src and connect-src.
- Do not change unrelated code.

When you finish, list the files you changed and how I verify each path:
- Run php artisan view:clear if needed.
- Open a public page and inspect the head.
- Refresh Human Analytics status.
- Run php artisan optimize:clear after adding env values.
- Request a page with curl -A "GPTBot".
- Refresh AI Analytics status.

Research Notes#

  • Laravel 13.x configures middleware in bootstrap/app.php.
  • Middleware can run before and after $next($request), which makes final response status available.
  • The HTTP facade is the recommended built-in client for outbound server requests.

Primary references:

Human Analytics#

Add the script to the shared Blade layout.

TXT
<!-- resources/views/layouts/app.blade.php --><head>  <meta charset="utf-8">  <script    defer    data-write-key="vha_pk_..."    src="https://app.virent.app/js/script.js"  ></script>  @vite(['resources/css/app.css', 'resources/js/app.js'])</head>

Best practices:

  • Install in the layout used by public web routes.
  • Use only the publishable browser key in Blade output.
  • Add nonce attributes if the app's CSP requires them.
  • Clear compiled views if the deployed page does not update.

Verification:

SH
php artisan view:clear
  • Open a public page and inspect the head.
  • Check Network for the Virent script and pageview request.
  • Refresh Human Analytics status.

Troubleshooting:

  • If some pages are not tracked, find additional Blade layouts.
  • If CSP blocks Virent, update Laravel or proxy headers.
  • If cached views hide the change, run php artisan view:clear.

Ad blockers and privacy tools often block third-party analytics hosts, which silently drops visits. Relay the script and events through your own domain under /_vr, and load the script with data-api="/_vr" so events go to your domain too. The relay must forward the visitor's IP and country in x-virent-forwarded-for and x-virent-forwarded-country, with your website's secret key (vha_sk_…) in x-virent-proxy-key; without the key Virent ignores the forwarded headers and sees your server's network instead of your visitors'. Relay only /_vr/js/script.js and /_vr/v1/*, and never forward cookies or authorization headers. See First-party analytics for how Virent trusts these headers.

With nginx, add these locations to the site's server block:

TXT
location = /_vr/js/script.js {  proxy_pass https://app.virent.app/js/script.js;  proxy_set_header Host app.virent.app;  proxy_ssl_server_name on;  proxy_set_header Cookie "";}location /_vr/v1/ {  proxy_pass https://app.virent.app/v1/;  proxy_set_header Host app.virent.app;  proxy_ssl_server_name on;  proxy_set_header Cookie "";  proxy_set_header Authorization "";  proxy_set_header X-Virent-Forwarded-For $remote_addr;  # Your website's secret key: Virent trusts the forwarded IP only with it.  proxy_set_header X-Virent-Proxy-Key "vha_sk_...";}

On Cloudflare, deploy a Worker on the example.com/_vr/* route that copies only accept-language, content-type, origin and user-agent, sets x-virent-forwarded-for from cf-connecting-ip, x-virent-forwarded-country from request.cf.country and x-virent-proxy-key from a Worker secret holding your website's secret key, and fetches the same path without /_vr from https://app.virent.app. The dashboard install panel has the full Worker.

Then load the script from your domain:

HTML
<script  defer  data-api="/_vr"  data-write-key="vha_pk_..."  src="/_vr/js/script.js"></script>

AI Analytics#

Set server values:

ENV
VIRENT_SITE_ID=site_...VIRENT_INGEST_SECRET=vha_sk_...VIRENT_INGEST_ENDPOINT=https://app.virent.app/v1/ingest/bot

Create middleware:

TXT
<?phpnamespace App\Http\Middleware;use Closure;use Illuminate\Http\Request;use Illuminate\Support\Facades\Http;use Symfony\Component\HttpFoundation\Response;class TrackVirentCrawler{	public function handle(Request $request, Closure $next): Response	{		return $next($request);	}	public function terminate(Request $request, Response $response): void	{		$userAgent = $request->userAgent() ?? '';        if (            preg_match('/\.(?:png|jpg|jpeg|gif|svg|ico|webp|css|js|map|txt|xml)$/i', $request->path()) ||            ! preg_match('/GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|claudebot|Claude-Web|Claude-User|Claude-SearchBot|PerplexityBot|PerplexityBot\/|Perplexity-User|Perplexity-User\/|Google-CloudVertexBot|DeepSeekBot|DeepSeekSpider|CCBot|Bytespider|meta-externalagent|meta-webindexer|meta-externalfetcher|Amazonbot|DuckAssistBot|YouBot/i', $userAgent)        ) {			return;		}		try {			Http::withHeaders([				'x-api-key' => env('VIRENT_INGEST_SECRET'),			])->timeout(2)->post(env('VIRENT_INGEST_ENDPOINT'), [				'siteId' => env('VIRENT_SITE_ID'),				'url' => $request->fullUrl(),				'host' => $request->getHost(),				'path' => '/' . ltrim($request->path(), '/'),				'method' => $request->method(),				'referer' => $request->headers->get('referer'),				'userAgent' => $userAgent,				'statusCode' => $response->getStatusCode(),				'requestId' => (string) str()->uuid(),				'timestamp' => now()->toIso8601String(),			]);		} catch (\Throwable) {			// Analytics must never change the application response.		}    }}

Register it:

TXT
// bootstrap/app.phpuse App\Http\Middleware\TrackVirentCrawler;use Illuminate\Foundation\Configuration\Middleware;->withMiddleware(function (Middleware $middleware): void {    $middleware->web(append: [        TrackVirentCrawler::class,    ]);})

Best practices:

  • Append to Laravel's web middleware group for public page routes.
  • Do not include API routes unless AI crawler traffic to APIs should be tracked.
  • Use terminable middleware so telemetry is sent after the application response with its final status.
  • Clear config cache after env changes.

Verification:

SH
php artisan optimize:clearcurl -A "GPTBot" https://your-domain.com/
  • Confirm Laravel sends an outbound request to Virent.
  • Refresh AI Analytics status.

Troubleshooting:

  • If middleware does not run, confirm it is appended in bootstrap/app.php.
  • If env values are missing, clear config cache.
  • If high-traffic pages slow down, lower timeout values or queue telemetry.