Hi,
Are you able to map your web server web toor to the /public folder?
Here’s what ChatGPT suggests in case it’s helpful:
You’re right: this is Livewire being served from the wrong base path when the app lives in a subfolder. Livewire is trying to hit /livewire/update
at the domain root, but your install is at /inv
, so the endpoint should be /inv/livewire/update
(and the script should be /inv/livewire/livewire.js
). On cPanel/Apache with the NGINX Manager in front, you also need to make sure NGINX doesn’t “short-circuit” those dynamic routes.
Here’s a tight, subfolder-safe fix that works for Livewire v3 (and also lists v2 alternatives), plus the cPanel NGINX bit.
1) Make Laravel/Livewire aware of the subfolder
A. Confirm the basics
B. Livewire v3 (recommended): set the script & update routes at runtime
Add this to routes/web.php
(or AppServiceProvider::boot()
if you prefer):
use Illuminate\Support\Facades\Route;
use Livewire\Livewire;
// Serve Livewire's JS from /inv/livewire/livewire.js
Livewire::setScriptRoute(function ($handle) {
return Route::get('/inv/livewire/livewire.js', $handle)->name('livewire.js');
});
// Post updates to /inv/livewire/update
Livewire::setUpdateRoute(function ($handle) {
return Route::post('/inv/livewire/update', $handle)->name('livewire.update');
});
This forces Livewire to generate:
<script src="/inv/livewire/livewire.js" data-update-uri="/inv/livewire/update"></script>
(Why this works: Livewire 3 supports configuring its endpoints at runtime; this is the official pattern for non-root installs.) (Stack Overflow, Laravel)
C. Livewire v2 alternative (if you’re still on v2)
- Publish config:
php artisan livewire:publish --config
- In
config/livewire.php
, set:
'asset_url' => '/inv',
This prepends /inv
to /livewire/livewire.js
. (For v2, asset_url
is the documented way to support subdirectories.) (Livewire)
Tip: If you’ve previously published assets, you can also do
php artisan vendor:publish --force --tag=livewire:assets
then ensure they’re reachable under the subfolder. (mazer.dev)
2) Make NGINX (cPanel “NGINX Manager”) pass Livewire through
Because NGINX sits in front, it may try to serve /livewire/livewire.js
as a static file or cache /livewire/update
. Add a user include to bypass cache and route these URIs to Apache/PHP.
In your NGINX include for the domain (via cPanel NGINX Manager):
# Livewire endpoints under subfolder
location ^~ /inv/livewire/ {
# do not cache ajax updates or dynamic js
proxy_no_cache 1;
add_header X-Accel-Buffering no;
# If you proxy to Apache:
proxy_pass http://apache_upstream;
include proxy_params;
# OR if you run PHP-FPM directly, use try_files to index.php:
# try_files $uri /inv/index.php?$query_string;
}
(Exact upstream names vary per WHM setup. The key idea: don’t treat /inv/livewire/*
as static; forward to PHP.) Guides on Livewire 404s with NGINX call out this pitfall explicitly. (inyomanjyotisa.medium.com, Laravel Geek, GitHub)
3) Update Apache/.htaccess for subfolder rewrites
In /home/mydomain/public_html/inv/.htaccess
(or /public
if that’s your document root), ensure you have a RewriteBase
that matches the subfolder:
RewriteEngine On
RewriteBase /inv/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
This helps Laravel generate correct relative URLs when behind Apache+cPanel in a subdirectory (it’s a common gotcha with subfolder installs).
4) Verify
- Visit
https://mydomain.xyz/inv/livewire/livewire.js
in the browser — you should get a 200 OK (JS content).
- Open the invoice link as a client; in DevTools → Network you should see
POST /inv/livewire/update
returning 200, not 404.
- If still wrong, re-clear caches (
optimize:clear
) and check that your Blade layout includes @livewireScripts
on portal pages.
5) Edge cases (pick what applies)
-
cPanel “public” exposure: make sure the public entry point is /inv/public
(via a symlink or by moving the framework files one level up and keeping only public assets in webroot). Avoid serving the framework root directly.
-
SESSION path (rare, but in some cPanel proxies you may need):
SESSION_DRIVER=cookie
SESSION_SECURE_COOKIE=true
SESSION_PATH=/inv
-
Caching/CDN: if any CDN is in front, exclude /inv/livewire/*
from caching.
6) Why a subdomain often “just works”
Putting the app at a subdomain (e.g., invoices.mydomain.xyz
) avoids path prefix issues entirely and simplifies NGINX/Apache rules. If you’re able to switch later, it’s the lowest-friction long-term setup for Livewire-based portals. (Several Invoice Ninja forum threads end up recommending this.) (forum.invoiceninja.com)
Quick copy-paste checklist
.env
→ APP_URL=https://mydomain.xyz/inv
routes/web.php
→ add the Livewire::setScriptRoute and setUpdateRoute block above.
- NGINX Manager include → add the location ^~ /inv/livewire/ block to bypass static caching and forward to PHP/Apache.
.htaccess
→ ensure RewriteBase /inv/.
php artisan optimize:clear
- Reload services; re-test invoice in client portal.