{} CodeLift

Laravel + Vue Starter Kit: Docker-verified fork

We took the official laravel/vue-starter-kit (Inertia + Vue 3 + shadcn-vue + Fortify), ran it inside Docker, and published a production-hardened fork with the complete diff on GitHub. The finding set matches the React Starter Kit's, by des…

Pub 2026-04-19 Verified 2026-04-19 Upd 2026-04-19

Verification environment

  • PHP 8.5.5
  • Laravel 13.5.0
  • Composer 2.9.7
  • Node 22.22.2
  • npm 10.9.7
  • Frontend Vue 3 (Composition API) + Inertia
  • Database SQLite (tests)
  • OS Docker Desktop (php:8.5-cli-bookworm)

Laravel + Vue Starter Kit: Docker-verified fork

We took the official laravel/vue-starter-kit (Inertia + Vue 3 + shadcn-vue + Fortify), ran it inside Docker, and published a production-hardened fork with the complete diff on GitHub. The finding set matches the React Starter Kit's, by design — the Laravel backend is shared between the official starter kits — giving 8 commits on top of upstream and lifting the test suite from 40 passed to 44 passed / 151 assertions. This article is a standalone log of the Vue fork so Vue-focused readers don't have to cross-reference the React article.

Sister articles:

Target

Field Value
Name Laravel + Vue Starter Kit
Official URL https://github.com/laravel/vue-starter-kit
Stack Laravel 13 + Inertia + Vue 3 (Composition API) + shadcn-vue + Fortify
Upstream license MIT
Improvement license MIT

Verification date

2026-04-19

Environment

All steps ran inside a Docker Desktop container (CodeLift spec §5-6).

Item Value
Base image php:8.5-cli-bookworm + Node 22
PHP 8.5.5
Laravel Framework 13.5.0
Composer 2.9.7
Node / npm 22.22.2 / 10.9.7
Frontend Vue 3 (Composition API) + Inertia
Test DB SQLite
Upstream commit laravel/vue-starter-kit@1233a92

Baseline

docker compose run --rm app on upstream main: 40 passed (136 assertions), 22.99s. Vite build clean. Solid template, same quality as the React variant.

Given the Laravel backend is shared across the starter kits, every finding below lands in essentially the same file for both repos. Honest framing: these are the same issues, ported to this fork — because the Vue team needs the diff on its own upstream to be able to take it.

Improvements

Long-form rationale lives in the React sister article. This article lists the Vue-side changes directly.

A. npm run build fails opaquely when run before composer install

@laravel/vite-plugin-wayfinder shells out to php artisan wayfinder:generate. Without Composer deps, artisan can't boot. Error doesn't name composer install. Same as React.

Fix: Setup section in README.

B. .env.example ships dev defaults with no production hints

APP_DEBUG=true, LOG_LEVEL=debug, SESSION_ENCRYPT=false, no SESSION_SECURE_COOKIE.

Fix: inline comments showing the production-recommended values.

C. config/app.php hardcodes the timezone

config/app.php:68 is 'timezone' => 'UTC'. APP_TIMEZONE has no effect.

Fix:

-    'timezone' => 'UTC',
+    'timezone' => env('APP_TIMEZONE', 'UTC'),

D. No security response headers in the middleware stack

bootstrap/app.php appends only HandleAppearance, HandleInertiaRequests, AddLinkHeadersForPreloadedAssets. No CSP / HSTS / X-Content-Type-Options / X-Frame-Options / Referrer-Policy / Permissions-Policy.

Fix: new SetSecurityHeaders middleware appended to the web stack.

  • Always on: X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy: camera=(), microphone=(), geolocation=()
  • Production only: Strict-Transport-Security: max-age=31536000; includeSubDomains plus a CSP tuned for Inertia + Vite-built assets.

CSP keeps 'unsafe-inline' in script-srcthis is the same in Vue and React because Inertia inlines initial props into a <script> tag regardless of the frontend framework. Moving to a nonce-based CSP means changes in HandleInertiaRequests; out of scope here.

New test: tests/Feature/SecurityHeadersTest.php — 2 assertions.

E. No HTTPS scheme enforcement helper

Repo-wide grep for forceScheme, forceHttps, TrustProxies returns zero matches. Proxies that terminate TLS and forward plain http:// make Laravel generate http:// URLs.

Fix: three lines in AppServiceProvider::configureDefaults, guarded by isProduction().

if (app()->isProduction()) {
    URL::forceScheme('https');
}

F. Password rules — already handled upstream

Same as the React variant: AppServiceProvider::configureDefaults installs strong Password::defaults in production (min 12, mixed case, numbers, symbols, uncompromised()). No change. Documenting it so readers don't re-flag it.

G. Login rate limiter keys on email + IP only

Single-layer limit — IP rotation bypasses.

Fix: two-layer limit.

return [
    Limit::perMinute(5)->by($email.'|'.$ip),       // burst guard
    Limit::perHour(20)->by('login-account|'.$email), // account cumulative
];

H. Two-factor auth is available but not policy-enforced

Fortify feature enabled with full UI and tests. No admin-forced 2FA, no reminder banner. Design decision, not a defect. No code change.

I. All logs interleave into a single channel

config/logging.php ships the default stacksingle layout. Auth events interleave with queries and app logs.

Fix — two files:

  1. config/logging.php: add auth channel (daily, 90-day retention, env-tunable).
  2. app/Listeners/AuthActivitySubscriber.php: new subscriber, registered via Event::subscribe in AppServiceProvider::boot. Subscribes to Illuminate\Auth\Events\Registered|Login|Logout|Failed|PasswordReset + Fortify 2FA enable/disable. Structured info records with user_id, email, ip, user_agent.

New test: tests/Feature/Auth/AuthLoggingTest.php — 2 assertions.

J. Only the password endpoint carries an explicit settings throttle

routes/settings.php throttles only settings/password. profile.update and profile.destroy fall back to framework defaults.

Fix: throttle:10,1 on profile update, throttle:3,1 on profile destroy (account deletion is terminal).

Before / after

Dimension Official Improved
php artisan test 40 passed / 136 assertions 44 passed / 151 assertions
Setup-order documentation None README Setup section
Production hints in .env.example None Inline comments
APP_TIMEZONE takes effect No (hardcoded) Yes
Baseline security headers None 4 always-on + HSTS / CSP in production
HTTPS scheme forcing None Production-only URL::forceScheme('https')
Login rate limit Email + IP Email + IP and email-only
Auth event logging Mixed in default log Dedicated auth daily channel
Settings endpoint rate limit Password only + profile update / destroy

When this improvement fits

  • You're starting a new production product on top of laravel/vue-starter-kit and want the security + observability floor set before shipping features.
  • You already scaffolded and want a cherry-pickable production-hardening checklist.
  • You're deciding between Vue and React starter kits: the backend hardening cost is the same for both. Pick by frontend preference, not by what's missing here.

Choosing between Vue and React variants

The Laravel-side production work is identical. Pick the starter based on team familiarity, component library taste, and hiring signal. (A follow-up comparison article will put React / Vue / Livewire starters side-by-side on production readiness.)

Reproducing and adopting

Improvement branch: codelift-dev/vue-starter-kit#improvements.

git clone https://github.com/codelift-dev/vue-starter-kit.git
cd vue-starter-kit
git checkout improvements
docker compose -f codelift/docker-compose.yml build
docker compose -f codelift/docker-compose.yml run --rm app

Application-level diff only (excluding the CodeLift codelift/ directory):

git diff main improvements -- . ':!codelift'

Each finding is a standalone commit, cherry-pickable.

License

  • Upstream: MIT (Laravel LLC and contributors)
  • Improvement: MIT (CodeLift / JIT Inc.)

Findings reflect the state on the verification date; upstream may change.

Featured in comparisons

Related articles