What's the correct way to host a Laravel application at a sub-directory?

1 day ago 2
ARTICLE AD BOX

I want to host my Laravel application at https://my-domain.com/demo/. I've already got Nginx running on https://my-domain.com/ so have added the following to the config to proxy traffic to /demo to my app server:

upstream demo_app { server 10.x.x.x:8080; keepalive 16; } server { location = /demo { return 301 /demo/; } location ^~ /demo/ { proxy_pass http://demo_app/; # Pass original host + protocol so Laravel can build correct URLs proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; # Optional but useful: tell the app it's mounted under a prefix proxy_set_header X-Forwarded-Prefix /demo; proxy_http_version 1.1; proxy_set_header Connection ""; } # Rest of the server config }

I thought I could just add the my-domain.com server to TrustProxies.php and set APP_URL and VITE_BASE_PATH to include the subdirectory and everything would work, but I've found I need to create a urlResolver in my middleware to manually generate the correct URLs in order for the correct URLs to be generated and followed:

class HandleInertiaRequests extends Middleware { /** * The root template that is loaded on the first page visit. * * @var string */ protected $rootView = 'app'; /** * Determine the current asset version. */ public function version(Request $request): ?string { return parent::version($request); } /** * Define the props that are shared by default. * * @return array<string, mixed> */ public function share(Request $request): array { return [ ...parent::share($request), 'auth' => [ 'user' => $request->user(), ], 'ziggy' => fn() => [ ...(new Ziggy)->toArray(), 'url' => rtrim(config('app.url'), '/'), 'location' => $request->url(), ], 'flash' => [ 'error_message' => fn() => $request->session()->get('error_message'), ], 'active_route' => [ 'current' => Route::currentRouteName(), ], ]; } public function urlResolver() { return function (Request $request) { $prefix = rtrim($request->headers->get('X-Forwarded-Prefix', ''), '/'); // Path and query as Laravel sees them (after proxy_pass) $path = $request->getPathInfo(); $query = $request->getQueryString(); // Build the URL Inertia should think we're at $url = ($prefix ? $prefix : '') . $path; if ($query) { $url .= '?' . $query; } return $url; }; } }

I've added the front proxy and header to TrustProxies.php too:

<?php namespace App\Http\Middleware; use Illuminate\Http\Middleware\TrustProxies as Middleware; use Illuminate\Http\Request; class TrustProxies extends Middleware { /** * The trusted proxies for this application. * * @var array<int, string>|string|null */ protected $proxies = [ '10.0.0.0/8', ]; /** * The headers that should be used to detect proxies. * * @var int */ protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_PREFIX | Request::HEADER_X_FORWARDED_AWS_ELB; }

I'm new to Laravel, and while this works, it feels like a bit of a hack. Does anyone know if there is a better way to do this so that Ziggy, Intertia, Vite and every other part of the toolchain know that this app is running at a subdirectory?

Read Entire Article