0

I have a Next.js application deployed on Vercel and a WordPress blog running on an external server (nginx/Apache). I’d like all requests to /blog/* on my Next.js app to be proxied or redirected to the external server.

However, Vercel automatically strips trailing slashes from every URL. My WordPress setup enforces trailing slashes, so it issues a 301 Moved Permanently to the URL with the slash. Vercel then removes the slash again, triggering another 301 from WordPress—and so on, 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 stopped the slash‑removal ↔ slash‑addition ping‑pong.

// vercel.json

{
  
  "redirects": [
    {
      "source": "/blog/", // Match requests specifically ending with /blog/
      "destination": "https://my-wp-blog.com/blog/", // Ensure destination has the needed slash
      "permanent": true // Or false (use 301/302 if temporary, 307/308 if permanent - true often defaults to 308)
    },
    
  ]
}

// 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()
  
  // Force trailing slash for all blog paths
  if (url.pathname.startsWith('/blog') && !url.pathname.endsWith('/')) {
    const redirectUrl = new URL(request.url)
    redirectUrl.pathname = `${url.pathname}/`
    return NextResponse.redirect(redirectUrl, 308) // 308 Permanent Redirect preserves method
  }
  
  // Continue with proxying for requests that already have trailing slashes
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('host', 'my-wp-blog.com')
  
  const clientIP = request.ip || request.headers.get('x-forwarded-for') || ''
  requestHeaders.set('x-real-ip', clientIP)
  requestHeaders.set('x-forwarded-for', clientIP)
  
  requestHeaders.set('x-forwarded-proto', request.nextUrl.protocol.replace(':', ''))
  requestHeaders.set('x-forwarded-host', 'my-wp-blog.com')
  
  // Also explicitly ensure the proxied URL has a trailing slash
  const proxyUrl = new URL(request.url)
  proxyUrl.protocol = 'https:'
  proxyUrl.hostname = 'my-blog-server-ip'
  
  // Make sure the pathname still has the trailing slash when proxying
  if (!proxyUrl.pathname.endsWith('/')) {
    proxyUrl.pathname = `${proxyUrl.pathname}/`
  }
  
  return NextResponse.rewrite(proxyUrl, {
    request: {
      headers: requestHeaders,
    },
  })
}

export const config = {
  matcher: '/blog/:path*',
}

Example of the Loop (via curl -vIL):

> HEAD /blog/my-post/ HTTP/2
< HTTP/2 308  # Vercel removes the slash
< location: /blog/my-post

> HEAD /blog/my-post HTTP/2
< HTTP/2 301  # WordPress adds the slash
< location: https://example.com/blog/my-post/

…repeat indefinitely…

So my questions are:

  1. How can I disable Vercel/Next.js’s automatic trailing‑slash redirects entirely so that my middleware can fully control URL normalization?

  2. Is it possible to use both vercel.json and Edge Middleware together to achieve this, or must I choose one approach?

  3. Are there any additional headers or settings I need to tweak on Vercel or my external server to break out of this redirect loop?

Any guidance on a reliable pattern for proxying /blog to an external WordPress host—without triggering infinite redirects—would be greatly appreciated

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.