Pitchbar can run under a subfolder of your domain rather than a
dedicated subdomain — useful when you're sharing a domain with
other services (marketing site at the root, Pitchbar at
/app, status page at /status). Laravel
+ Vite handle this with config only; no code changes.
The walkthrough below assumes you want Pitchbar at
https://aichat.com/app. Substitute your own host +
path everywhere you see those values.
APP_URL
Open .env on the production server. Set:
APP_URL=https://aichat.com/app
# Session + Sanctum need the host (without the path).
SESSION_DOMAIN=aichat.com
SANCTUM_STATEFUL_DOMAINS=aichat.com
# Reverb websocket origin — same host:port as APP_URL.
REVERB_HOST=aichat.com
REVERB_PORT=443
REVERB_SCHEME=https
Run php artisan config:clear + php artisan
route:clear after editing.
public/
The Pitchbar repo is a Laravel application; the public docroot
must be public/, not the repo root. With a subfolder
install your web server needs to map the subpath to that
directory.
# /var/www/aichat.com/app -> Pitchbar repo
# /var/www/aichat.com/app/public is the docroot for /app.
Alias /app /var/www/aichat.com/pitchbar/public
<Directory /var/www/aichat.com/pitchbar/public>
AllowOverride All
Require all granted
</Directory>
server {
listen 443 ssl http2;
server_name aichat.com;
root /var/www/aichat.com/marketing-site; # your root site
# Pitchbar subfolder.
location ^~ /app/ {
alias /var/www/aichat.com/pitchbar/public/;
try_files $uri $uri/ @pitchbar;
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param SCRIPT_NAME /app/index.php;
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
}
}
location @pitchbar {
rewrite ^/app(/.*)$ /app/index.php?$1 last;
}
}
Vite reads APP_URL at build time to write the
correct manifest paths, so the admin SPA + widget bundle resolve
under the subfolder automatically. After deploy run:
npm ci
npm run build
npm run build:widget
Check public/build/manifest.json — the entries
should resolve under /app/build/... when served.
php artisan storage:link
Creates public/storage → storage/app/public.
Uploaded branding logos, public exports, and avatar files land
there; without the symlink they 404.
Customers embed the widget via a <script> tag
on their own site. The URL must point at your subfolder:
<script
src="https://aichat.com/app/widget/widget.js"
data-agent-id="agent_..."
defer
></script>
The admin's "Copy embed snippet" button on
/app/agents/{id} derives the URL from
APP_URL, so as long as step 1 is correct the snippet
your customers copy will already include the subfolder.
Both still run from the repo root, unaffected by the subfolder:
php artisan queue:work
php artisan schedule:work
The Cloudflare Worker cron driver (production default) hits
https://aichat.com/app/api/v1/internal/queue-tick —
update the worker's QUEUE_TICK_URL binding accordingly
when you deploy it from /admin/integrations/cron-worker.
/app
The web-server rewrite isn't sending Laravel's URLs back to
index.php. Confirm the Apache .htaccess
inside public/ is being read (Apache:
AllowOverride All) or the Nginx
@pitchbar fallback fires (Nginx:
try_files ordering).
The widget post-build emits a hashed filename
(widget.<hash>.js). The
data-agent-id snippet points at the unhashed
widget.js which is also published. If that 404s,
you skipped npm run build:widget after deploy.
If you serve the marketing site at aichat.com and
Pitchbar at aichat.com/app, both share the same
cookie host but different paths. Set SESSION_PATH=/app
in .env so the Laravel session cookie is scoped to
the subfolder — without this the cookie set by Pitchbar may be
overwritten by the parent site's session library.
The default CSP in AddSecurityHeaders middleware
allows self only for scripts. If you host static
assets on a CDN, add the CDN host to script-src
via the app_security.csp_extra_script_src config
key.
Reverb listens on its own port (default 8080). Your reverse proxy must proxy the WebSocket upgrade — for Nginx that's:
location /reverb/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
APP_URL, so they
automatically include the subfolder. Re-register them in the
respective dashboards if you migrated from a different host.allowed_origins on
the agent — that's the origin of the customer's site, NOT
your Pitchbar host. Subfolder install doesn't affect which
sites can embed.