NextResponse는 Web Response API를 확장하여 추가적인 편의 메서드를 제공한다.
cookies응답의 Set-Cookie 헤더를 읽거나 수정한다.
set(name, value) — 이름과 값을 지정해 응답에 쿠키를 설정한다.
// 요청 경로: /home
let response = NextResponse.next()
// 응답에 `Set-Cookie: show-banner=false; path=/home` 헤더가 추가된다.
response.cookies.set('show-banner', 'false')
return response
get(name) — 쿠키 이름으로 값을 반환한다. 쿠키가 없으면 undefined를 반환하고, 동일한 이름의 쿠키가 여러 개이면 첫 번째 값을 반환한다.
// 요청 경로: /home
let response = NextResponse.next()
// { name: 'show-banner', value: 'false', Path: '/home' }
response.cookies.get('show-banner')
getAll() — 이름을 지정하면 해당 쿠키의 모든 값을 반환한다. 이름을 생략하면 응답의 모든 쿠키를 반환한다.
// 요청 경로: /home
let response = NextResponse.next()
// [
// { name: 'experiments', value: 'new-pricing-page', Path: '/home' },
// { name: 'experiments', value: 'winter-launch', Path: '/home' },
// ]
response.cookies.getAll('experiments')
// 응답의 모든 쿠키를 가져올 수도 있다.
response.cookies.getAll()
has(name) — 지정한 이름의 쿠키가 응답에 존재하면 true를 반환한다.
// 요청 경로: /home
let response = NextResponse.next()
// 쿠키가 존재하면 true, 없으면 false를 반환한다.
response.cookies.has('experiments')
delete(name) — 이름으로 지정한 쿠키를 응답에서 삭제한다.
// 요청 경로: /home
let response = NextResponse.next()
// 삭제되면 true, 삭제할 쿠키가 없으면 false를 반환한다.
response.cookies.delete('experiments')
json()지정한 JSON 본문으로 응답을 생성한다.
// app/api/route.ts
import { NextResponse } from 'next/server'
export async function GET(request: Request) {
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 })
}
redirect()지정한 URL로 리다이렉트하는 응답을 생성한다.
import { NextResponse } from 'next/server'
return NextResponse.redirect(new URL('/new', request.url))
NextResponse.redirect()에 전달하기 전에 URL을 생성하거나 수정할 수 있다. 예를 들어 request.nextUrl로 현재 URL을 가져온 뒤 변경해 다른 경로로 리다이렉트할 수 있다.
import { NextResponse } from 'next/server'
const loginUrl = new URL('/login', request.url)
// /login URL에 ?from=/현재경로 쿼리를 추가한다.
loginUrl.searchParams.set('from', request.nextUrl.pathname)
return NextResponse.redirect(loginUrl)
rewrite()브라우저에 표시되는 URL은 원본 경로로 유지하면서, 실제 요청은 지정한 URL로 프록시하는 응답을 생성한다.
import { NextResponse } from 'next/server'
// 브라우저에는 /about이 표시되지만, 실제 요청은 /proxy로 처리된다.
return NextResponse.rewrite(new URL('/proxy', request.url))
next()미들웨어에서 조기 반환 후 라우팅을 계속 진행할 때 사용한다.
import { NextResponse } from 'next/server'
return NextResponse.next()
NextResponse.next({ request: { headers } })를 통해 수정된 헤더를 업스트림으로 전달할 수도 있다.
import { NextResponse } from 'next/server'
const newHeaders = new Headers(request.headers)
newHeaders.set('x-version', '123')
return NextResponse.next({
request: {
headers: newHeaders,
},
})
이 방식은 수정된 헤더를 대상 페이지, 라우트, 서버 액션으로 전달하며 클라이언트에는 노출되지 않는다. 업스트림으로 데이터를 전달할 때 유용하지만, 해당 헤더가 외부 서비스로 전달될 수 있으므로 주의가 필요하다.
NextResponse.next({ headers })는 프록시에서 클라이언트로 헤더를 직접 전송하는 단축 표현으로, 사용을 권장하지 않는다.Content-Type과 같은 응답 헤더를 덮어쓸 경우 서버 액션 요청 실패나 스트리밍 응답 오류가 발생할 수 있다.
import { type NextRequest, NextResponse } from 'next/server'
async function proxy(request: NextRequest) {
const headers = await injectAuth(request.headers)
// 이 방식은 사용하지 않는다.
return NextResponse.next({ headers })
}
수신된 요청 헤더를 그대로 복사하면 민감한 데이터가 클라이언트나 외부 서비스로 유출될 수 있다. 허용 목록(allow-list) 방식으로 안전한 헤더만 선별해 전달하는 방어적 접근을 권장한다.
import { type NextRequest, NextResponse } from 'next/server'
function proxy(request: NextRequest) {
const incoming = new Headers(request.headers)
const forwarded = new Headers()
for (const [name, value] of incoming) {
const headerName = name.toLowerCase()
// x-* 커스텀 헤더, authorization, cookie는 제외하고 안전한 헤더만 전달한다.
if (
!headerName.startsWith('x-') &&
headerName !== 'authorization' &&
headerName !== 'cookie'
) {
forwarded.set(name, value)
}
}
return NextResponse.next({
request: {
headers: forwarded,
},
})
}
X-System-Arch (커스텀 헤더): 실무 시스템에서는 HTTP 헤더에 X-로 시작하는 사용자 정의 헤더를 주입하곤 한다. 수많은 서버가 얽힌 마이크로서비스 환경에서 "이 응답이 어느 서버, 어느 모듈에서 처리되었는지" 꼬리표를 달아 시스템을 추적(Tracing)하고 모니터링하기 위한 아키텍트의 고급 통제 기술이다.