
September 08, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
Choosing how to version a REST API locks in client contracts for years. When API versioning strategies compared side by side, the trade-offs become obvious: URL paths are simple to debug, headers keep URLs clean, and query parameters sit somewhere in between. I've shipped public APIs on Laravel 12 and 13 for legal-tech portals, eCommerce carts, and partner integrations — and the wrong versioning choice usually surfaces months later, during a breaking change nobody planned for. This guide walks through each approach with copy-pasteable patterns, a decision table, and the deprecation rules that keep production stable.
/v1/) wins for public REST APIs; Accept-header versioning suits strict content negotiation; query params work for internal tools; avoid mixing strategies on one surface.Before you pick a strategy, read how building RESTful APIs with Laravel structures routes and resources. Public APIs also need auth, throttling, and docs — topics covered in our API development service and companion posts on Laravel API best practices.
How Do API Versioning Strategies Compare for REST APIs?
Every versioning method answers one question: how does a client request a specific contract? The answer affects caching, SDK design, gateway rules, and how painful breaking changes become.
Breaking changes include removing fields, renaming properties, changing validation rules, or altering HTTP semantics. Non-breaking additions — new optional fields, new endpoints — typically stay on the current version. The strategy you choose should make the version visible at the edge and traceable in logs.
| Strategy | Example | Client friction | Cache-friendly | Best fit |
|---|---|---|---|---|
| URI path | /api/v1/orders | Low | High | Public REST APIs, mobile apps, third-party SDKs |
| Query parameter | /api/orders?version=2 | Low | Medium | Internal tools, gradual rollout behind flags |
| Accept header | Accept: application/vnd.app.v2+json | Medium | Medium | Strict hypermedia APIs, vendor media types |
| Custom header | Api-Version: 2 | Medium | Low | When URLs must stay identical across versions |
| Subdomain | v2.api.example.com | High | High | Large platforms with separate deploy units |
| No explicit version | Additive-only evolution | Lowest | High | Early-stage products with few external clients |
For most teams shipping Laravel 13 on PHP 8.3+, URI path versioning is the default I'd recommend. It shows up clearly in access logs, works with any HTTP client, and aligns with how Stripe, GitHub, and Shopify document their Admin API (2026-07 or later).
What Is URI Path Versioning and When Should You Use It?
URI path versioning embeds the major version in the URL segment. Clients call /api/v1/users today and migrate to /api/v2/users when ready. Proxies, CDNs, and WAF rules can route or block by path prefix without parsing headers.
Laravel 13 route groups
In Laravel 13, group versioned routes under a prefix and namespace. Keep v1 and v2 in separate controller directories so diffs stay readable during refactors.
// routes/api.php
Route::prefix('v1')->group(base_path('routes/api/v1.php'));
Route::prefix('v2')->group(base_path('routes/api/v2.php'));
// routes/api/v1.php
use App\Http\Controllers\Api\V1\UserController;
Route::get('/users', [UserController::class, 'index']);
Route::get('/users/{user}', [UserController::class, 'show']);
// routes/api/v2.php
use App\Http\Controllers\Api\V2\UserController;
Route::get('/users', [UserController::class, 'index']);
Use separate Form Requests and API Resources per version. Sharing a single Resource class between v1 and v2 creates hidden coupling — a field rename in v2 breaks v1 serialization silently.
When path versioning wins
- Public partner APIs where clients copy URLs from documentation
- Mobile apps that hard-code base URLs per release
- API gateways (Kong, Traefik) that rate-limit by path prefix — see our Kong API gateway guide
- Teams that publish OpenAPI specs per major version
On a legal-tech client portal I built, path versioning let the mobile team stay on /v1/ while the web dashboard moved to /v2/ with expanded document metadata. Logs made support tickets easy to triage.
How Does Header-Based API Versioning Work?
Header-based versioning keeps URLs stable. The client sends an Accept header or a custom header like Api-Version: 2. The server selects the response shape through content negotiation defined in RFC 7231 section 5.3.2.
Custom header middleware in Laravel
// app/Http/Middleware/ResolveApiVersion.php
public function handle(Request $request, Closure $next): Response
{
$version = $request->header('Api-Version', '1');
$request->attributes->set('api_version', $version);
return $next($request);
}
// app/Http/Controllers/Api/UserController.php
public function show(Request $request, User $user): JsonResponse
{
return match ($request->attributes->get('api_version')) {
'2' => UserResourceV2::make($user)->response(),
default => UserResourceV1::make($user)->response(),
};
}
Vendor media types follow the pattern application/vnd.{vendor}.{version}+json. GitHub's REST API uses this approach. Clients must set headers correctly — a common support burden when integrators test from browser address bars.
Header versioning suits B2B APIs where every client runs a maintained SDK. It fails fast when casual integrators paste URLs into Postman without configuring headers. Pair it with clear docs — our post on API documentation with Scribe for Laravel covers generating examples per version.
Which API Versioning Strategy Fits Laravel 13 Best?
Laravel 13 on PHP 8.3+ gives you clean route groups, middleware, API Resources, and Form Requests. The framework does not mandate a versioning style — your contract design drives the choice.
Practical Laravel 13 layout
- Pick one primary strategy per API surface — usually URI path.
- Namespace controllers:
App\Http\Controllers\Api\V1andV2. - Publish separate OpenAPI documents per major version.
- Apply rate limiting per version if v1 clients behave differently from v2.
- Authenticate with Sanctum or Passport — tokens are version-agnostic unless you scope them.
- Write contract tests — Pact contract testing catches breaking diffs before deploy.
For Symfony 8.1 projects, the same principles apply via route prefixes and serializer groups. Symfony's serializer groups map cleanly to version-specific output shapes — see Symfony serializer for API responses.
Query-parameter versioning (?version=2) works for feature flags and beta endpoints. I use it sparingly on admin-only routes where the audience is one frontend team we control. Public APIs on platforms like Quick And Easy Nepalese Grocery stick to path prefixes because delivery-partner integrations need obvious URLs.
Version numbering rules
Increment the major version only on breaking changes. Minor and patch numbers belong in changelogs and response headers — not necessarily in the URL. A practical response header:
X-API-Version: 2.3.1
Sunset: Sat, 01 Mar 2027 00:00:00 GMT
Deprecation: true
Link: <https://docs.example.com/migrate-v2-to-v3>; rel="deprecation"
Stripe pins dated API versions per account — a different model worth studying in their official versioning documentation. Account-level pinning shifts migration cost to the provider; URL major versions shift it to the client.
How Do Query Parameter and Subdomain Versioning Compare?
Query-parameter versioning appends ?version=2 or ?api-version=2 to an unchanged path. Proxies may strip query strings from cache keys incorrectly if not configured — test CDN behaviour before production.
// routes/api.php — query param fallback (internal APIs only)
Route::get('/reports/summary', function (Request $request) {
$version = $request->query('version', '1');
return match ($version) {
'2' => app(SummaryControllerV2::class)->index(),
default => app(SummaryControllerV1::class)->index(),
};
});
Subdomain versioning (v2.api.example.com) suits organisations running separate deploy units per version. DNS, TLS certificates, and CORS policies multiply. I've seen this on large microservice estates described in our monolith-to-microservices migration guide — not typical for a Laravel monolith on a single VPS.
How Do You Deprecate and Sunset Old API Versions Safely?
Versioning without a deprecation plan creates zombie endpoints nobody dares remove. Define a published policy: minimum notice period, sunset headers, and a hard removal date.
Follow the full playbook in API deprecation and sunset best practices. Key steps I repeat on every project:
- Log requests by version and client ID — know who still calls v1.
- Add
SunsetandDeprecationresponse headers months before removal. - Publish a migration guide with field mapping tables — use our JSON formatter in examples so diffs are readable.
- Never reuse version numbers after sunset — v3 follows v2, not a recycled v1.
- Run Pact or Postman collections in CI so v2 parity is proven — see API testing with Postman and Newman.
On payment integrations — eSewa, Khalti, Stripe — webhook payloads often cannot break silently. Version webhook handlers separately and accept both formats during transition. Our OpenAI API integration guide shows the same pattern for upstream API drift.
If you publish an SDK, version it alongside the API — guidance in SDK design for your public API. Mijar Law Associates' client portal kept v1 mobile endpoints alive for nine months while law firms migrated document upload flows to v2.
What Mistakes Break API Versioning in Production?
These failures show up repeatedly across client projects and are cheap to avoid at design time.
Mixing URI and header versioning on the same resource confuses clients and breaks cache rules — pick one primary signal.
- Leaking v2 fields into v1 responses — use separate API Resource classes, not conditional branches scattered in controllers.
- Breaking changes without a major bump — renaming
phonetomobileis breaking even if the database column changed months ago. - No default version — undocumented defaults become undeletable. Document whether bare
/api/usersmaps to v1 or returns 400. - Forgetting gateway config — WAF rules, CORS, and rate limits must include new prefixes. Read Kong vs Traefik vs AWS API Gateway for edge routing patterns.
- Skipping changelog discipline — treat API changelogs like database migrations; tie each entry to a version bump.
For long-lived platforms, schedule version reviews during support and maintenance retainers. Laravel 11 reached EOL in March 2026 — upgrading to Laravel 12 or 13 is a good moment to audit which API versions still earn their keep.
Key Takeaways
- URI path versioning (
/api/v1/) is the best default for public REST APIs — visible in logs, docs, and SDKs. - Header and vendor media-type versioning fits SDK-controlled B2B clients when URLs must stay constant.
- Increment major versions only on breaking changes; publish Sunset headers at least six months before removal.
- Separate Laravel controllers, Form Requests, and API Resources per version — never share serialization logic.
- Run contract tests in CI and monitor per-version traffic before sunsetting old endpoints.
- Document one strategy per API surface; mixing path and header signals creates support and caching bugs.
People Also Ask
What is the most common API versioning strategy?
URI path versioning is the most widely used approach for public REST APIs. Major vendors expose /v1/ and /v2/ path segments because they are easy to document, debug in access logs, and configure at the gateway layer without custom header parsing.
Should API versioning go in the URL or header?
Use the URL for public APIs with diverse clients — mobile apps, partner integrations, no-code tools. Use headers when every consumer runs your SDK and you need a stable URL for caching or legacy compatibility. Headers add friction for manual testing but keep paths clean.
How long should you support deprecated API versions?
Most production teams allow six to twelve months between deprecation announcement and hard removal. Monitor active traffic throughout; if enterprise clients still call the old version, extend the window rather than breaking payroll, payment, or compliance workflows without notice.
Does GraphQL need the same versioning as REST?
GraphQL APIs often favour additive schema evolution and field deprecation directives instead of URL major versions. REST's resource-oriented contracts map more naturally to explicit /v1/ and /v2/ boundaries — especially when external clients cache full response shapes.
Pick a Strategy and Document It Today
API versioning strategies compared on real criteria — client friction, cache behaviour, gateway fit, deprecation cost — point most Laravel and Symfony teams toward URI path prefixes for public surfaces. Lock the choice in your OpenAPI spec, enforce it in code review, and plan sunset headers before v2 ships. Need help designing a versioned API for a portal, marketplace, or payment integration? Contact us or explore API development in Nepal — and browse the Mijar Law Associates portfolio for a client portal that runs versioned document APIs in production.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

