I have a Next.js app deployed on Vercel and a WordPress blog running on an external server (nginx/Apache). I want every request to /blog/* on my Next.js site to be proxied to the external blog, but Vercel automatically strips trailing slashes while WordPress enforces them. This results in an infinite redirect loop:
Vercel strips the slash:
GET /blog/my-post/
← HTTP/2 308
← location: /blog/my-post
WordPress redirects to add the slash:
GET /blog/my-post
← HTTP/2 301
← location: https://example.com/blog/my-post/
Back to step 1… ad infinitum.
What I’ve Tried
- vercel.json rewrite/redirect rules
- next.config.js with trailingSlash: true / false
- middleware.ts (Next.js Edge Middleware) to normalize slashes and proxy headers
None of these approaches have broken the slash‑removal ↔ slash‑addition ping‑pong.
configuration snippets I have tried:
vercel.json
{
"redirects": [
{
"source": "/blog/",
"destination": "https://my-wp-blog.com/blog/",
"permanent": true
}
]
}
next.config.js
module.exports = {
trailingSlash: true,
}
middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const url = request.nextUrl.clone()
// 1. Force trailing slash on all /blog paths
if (url.pathname.startsWith('/blog') && !url.pathname.endsWith('/')) {
url.pathname += '/'
return NextResponse.redirect(url, 308)
}
// 2. Proxy to external WordPress server
const headers = new Headers(request.headers)
headers.set('host', 'my-wp-blog.com')
const clientIP = request.ip || request.headers.get('x-forwarded-for') || ''
headers.set('x-real-ip', clientIP)
headers.set('x-forwarded-for', clientIP)
headers.set('x-forwarded-proto', request.nextUrl.protocol.replace(':', ''))
headers.set('x-forwarded-host', 'my-wp-blog.com')
const proxyUrl = new URL(request.url)
proxyUrl.protocol = 'https:'
proxyUrl.hostname = 'my-blog-server-ip'
if (!proxyUrl.pathname.endsWith('/')) {
proxyUrl.pathname += '/'
}
return NextResponse.rewrite(proxyUrl, { request: { headers } })
}
export const config = {
matcher: '/blog/:path*',
}
None of these proposed solutions worked! Any help, would be appreciated.