如何正确在子目录部署Laravel应用?
我要把Laravel应用部署在https://my-domain.com/demo/,当前已有Nginx运行在https://my-domain.com/,所以在Nginx配置里加了以下内容,把/demo的流量代理到应用服务器:
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 }
我原本以为只要把my-domain.com服务器添加到TrustProxies.php,再把APP_URL和VITE_BASE_PATH设成包含子目录就能正常运行,但发现必须在中间件里写urlResolver手动生成正确URL,才能保证URL生成和跳转正常:
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; }; } }
我也已经把前端代理和头部信息加到TrustProxies.php里了:
<?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; }
我是Laravel新手,当前方案能运行但不够优雅,想知道有没有更优方案,能让Ziggy、Inertia、Vite及其他工具链自动识别应用运行在子目录?
不需要手动编写urlResolver,通过以下配置可以让整个工具链自动识别子目录:
1. Laravel 核心配置
确保APP_URL正确设置为子目录地址:
APP_URL=https://my-domain.com/demo
你的TrustProxies.php配置已经正确包含了X-Forwarded-Prefix,Laravel会自动读取这个头部来修正请求的URL信息,无需额外处理。
2. Ziggy 配置
Ziggy可以直接读取APP_URL和请求的前缀信息,不需要在share方法里手动覆盖url和location。只需确保在前端正确引入Ziggy的配置,或者在后端共享时直接传递默认的Ziggy数组:
'ziggy' => fn() => (new Ziggy)->toArray(),
如果需要更灵活的配置,可以在config/ziggy.php里设置prefix:
return [ 'prefix' => env('APP_URL') !== 'http://localhost' ? '/demo' : '', // 其他配置... ];
3. Inertia 配置
Inertia会自动使用Laravel生成的正确URL,只要Laravel本身能识别子目录(通过X-Forwarded-Prefix和APP_URL),就不需要自定义urlResolver。移除你添加的urlResolver方法即可,Inertia会自动处理URL生成。
4. Vite 配置
确保VITE_BASE_PATH设置正确:
VITE_BASE_PATH="/demo"
然后在vite.config.js里配置base选项:
import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; export default defineConfig({ plugins: [ laravel({ input: ['resources/css/app.css', 'resources/js/app.js'], refresh: true, }), ], base: process.env.VITE_BASE_PATH, });
在Blade模板里引入Vite资源时,使用Laravel的@vite指令即可,它会自动加上前缀:
@vite(['resources/css/app.css', 'resources/js/app.js'])
5. 路由配置
不需要修改路由文件,Laravel会自动处理子目录的路由解析,因为Nginx已经把/demo/下的请求代理到应用根目录,应用里的路由可以保持原本的写法(比如Route::get('/', ...)对应https://my-domain.com/demo/)。
内容的提问来源于stack exchange,提问作者MorayM

