Server Actions und Mutationen

Server Actions sind asynchrone Funktionen, die auf dem Server ausgeführt werden. Sie können in Server- und Client-Komponenten verwendet werden, um Formularübermittlungen und Datenmutationen in Next.js-Anwendungen zu handhaben.

🎥 Video: Mehr über Formulare und Mutationen mit Server Actions erfahren → YouTube (10 Minuten).

Konvention

Eine Server Action kann mit der React "use server"-Direktive definiert werden. Sie können die Direktive am Anfang einer async-Funktion platzieren, um die Funktion als Server Action zu markieren, oder am Anfang einer separaten Datei, um alle Exporte dieser Datei als Server Actions zu markieren.

Server-Komponenten

Server-Komponenten können die Inline-Funktions- oder Modul-Ebene der "use server"-Direktive verwenden. Um eine Server Action inline zu definieren, fügen Sie "use server" am Anfang des Funktionskörpers hinzu:

// Server-Komponente
export default function Page() {
  // Server Action
  async function create() {
    'use server'

    // ...
  }

  return (
    // ...
  )
}
// Server-Komponente
export default function Page() {
  // Server Action
  async function create() {
    'use server'

    // ...
  }

  return (
    // ...
  )
}

Client-Komponenten

Client-Komponenten können nur Actions importieren, die die Modul-Ebene der "use server"-Direktive verwenden.

Um eine Server Action in einer Client-Komponente aufzurufen, erstellen Sie eine neue Datei und fügen Sie die "use server"-Direktive am Anfang hinzu. Alle Funktionen innerhalb der Datei werden als Server Actions markiert, die sowohl in Client- als auch Server-Komponenten wiederverwendet werden können:

'use server'

export async function create() {
  // ...
}
'use server'

export async function create() {
  // ...
}
import { create } from '@/app/actions'

export function Button() {
  return (
    // ...
  )
}
import { create } from '@/app/actions'

export function Button() {
  return (
    // ...
  )
}

Sie können eine Server Action auch als Prop an eine Client-Komponente übergeben:

<ClientComponent updateItem={updateItem} />
app/client-component.jsx
'use client'

export default function ClientComponent({ updateItem }) {
  return <form action={updateItem}>{/* ... */}</form>
}

Verhalten

  • Server Actions können mit dem action-Attribut in einem <form>-Element aufgerufen werden:
    • Server-Komponenten unterstützen standardmäßig progressive Verbesserung, d.h. das Formular wird auch dann übermittelt, wenn JavaScript noch nicht geladen oder deaktiviert ist.
    • In Client-Komponenten werden Formulare, die Server Actions aufrufen, in eine Warteschlange gestellt, falls JavaScript noch nicht geladen ist, wobei die Client-Hydration priorisiert wird.
    • Nach der Hydration wird der Browser bei der Formularübermittlung nicht neu geladen.
  • Server Actions sind nicht auf <form> beschränkt und können von Event-Handlern, useEffect, Drittanbieter-Bibliotheken und anderen Formularelementen wie <button> aufgerufen werden.
  • Server Actions integrieren sich in die Next.js Caching- und Revalidierungsarchitektur. Wenn eine Action aufgerufen wird, kann Next.js sowohl die aktualisierte UI als auch neue Daten in einem einzigen Server-Roundtrip zurückgeben.
  • Hinter den Kulissen verwenden Actions die POST-Methode, und nur diese HTTP-Methode kann sie aufrufen.
  • Die Argumente und Rückgabewerte von Server Actions müssen von React serialisierbar sein. Siehe die React-Dokumentation für eine Liste der serialisierbaren Argumente und Werte.
  • Server Actions sind Funktionen. Das bedeutet, sie können überall in Ihrer Anwendung wiederverwendet werden.
  • Server Actions erben die Laufzeitumgebung von der Seite oder dem Layout, in dem sie verwendet werden.
  • Server Actions erben die Route Segment Config von der Seite oder dem Layout, in dem sie verwendet werden, einschließlich Feldern wie maxDuration.

Beispiele

Formulare

React erweitert das HTML <form>-Element, um Server Actions mit der action-Prop aufrufen zu können.

Bei Aufruf in einem Formular erhält die Action automatisch das FormData-Objekt. Sie müssen React useState nicht verwenden, um Felder zu verwalten, sondern können die Daten mit den nativen FormData-Methoden extrahieren:

export default function Page() {
  async function createInvoice(formData: FormData) {
    'use server'

    const rawFormData = {
      customerId: formData.get('customerId'),
      amount: formData.get('amount'),
      status: formData.get('status'),
    }

    // Daten mutieren
    // Cache revalidieren
  }

  return <form action={createInvoice}>...</form>
}
export default function Page() {
  async function createInvoice(formData) {
    'use server'

    const rawFormData = {
      customerId: formData.get('customerId'),
      amount: formData.get('amount'),
      status: formData.get('status'),
    }

    // Daten mutieren
    // Cache revalidieren
  }

  return <form action={createInvoice}>...</form>
}

Gut zu wissen:

Zusätzliche Argumente übergeben

Sie können zusätzliche Argumente an eine Server Action übergeben, indem Sie die JavaScript bind-Methode verwenden.

'use client'

import { updateUser } from './actions'

export function UserProfile({ userId }: { userId: string }) {
  const updateUserWithId = updateUser.bind(null, userId)

  return (
    <form action={updateUserWithId}>
      <input type="text" name="name" />
      <button type="submit">Benutzernamen aktualisieren</button>
    </form>
  )
}
'use client'

import { updateUser } from './actions'

export function UserProfile({ userId }) {
  const updateUserWithId = updateUser.bind(null, userId)

  return (
    <form action={updateUserWithId}>
      <input type="text" name="name" />
      <button type="submit">Benutzernamen aktualisieren</button>
    </form>
  )
}

Die Server Action erhält das userId-Argument zusätzlich zu den Formulardaten:

app/actions.js
'use server'

export async function updateUser(userId, formData) {
  // ...
}

Gut zu wissen:

  • Eine Alternative ist, Argumente als versteckte Eingabefelder im Formular zu übergeben (z.B. <input type="hidden" name="userId" value={userId} />). Der Wert wird jedoch Teil des gerenderten HTML und ist nicht kodiert.
  • .bind funktioniert sowohl in Server- als auch Client-Komponenten. Es unterstützt auch progressive Verbesserung.

Ausstehende Zustände

Sie können den React useFormStatus-Hook verwenden, um einen ausstehenden Zustand anzuzeigen, während das Formular übermittelt wird.

  • useFormStatus gibt den Status für ein bestimmtes <form> zurück, daher muss es als Kind des <form>-Elements definiert werden.
  • useFormStatus ist ein React-Hook und muss daher in einer Client-Komponente verwendet werden.
'use client'

import { useFormStatus } from 'react-dom'

export function SubmitButton() {
  const { pending } = useFormStatus()

  return (
    <button type="submit" disabled={pending}>
      Hinzufügen
    </button>
  )
}
'use client'

import { useFormStatus } from 'react-dom'

export function SubmitButton() {
  const { pending } = useFormStatus()

  return (
    <button type="submit" disabled={pending}>
      Hinzufügen
    </button>
  )
}

<SubmitButton /> kann dann in jedes Formular eingebettet werden:

import { SubmitButton } from '@/app/submit-button'
import { createItem } from '@/app/actions'

// Server-Komponente
export default async function Home() {
  return (
    <form action={createItem}>
      <input type="text" name="field-name" />
      <SubmitButton />
    </form>
  )
}
import { SubmitButton } from '@/app/submit-button'
import { createItem } from '@/app/actions'

// Server-Komponente
export default async function Home() {
  return (
    <form action={createItem}>
      <input type="text" name="field-name" />
      <SubmitButton />
    </form>
  )
}

Serverseitige Validierung und Fehlerbehandlung

Wir empfehlen die Verwendung von HTML-Validierung wie required und type="email" für grundlegende clientseitige Formularvalidierung.

Für erweiterte serverseitige Validierung können Sie eine Bibliothek wie zod verwenden, um die Formularfelder vor der Datenmutation zu validieren:

'use server'

import { z } from 'zod'

const schema = z.object({
  email: z.string({
    invalid_type_error: 'Ungültige E-Mail',
  }),
})

export default async function createUser(formData: FormData) {
  const validatedFields = schema.safeParse({
    email: formData.get('email'),
  })

  // Frühzeitig zurückkehren, wenn die Formulardaten ungültig sind
  if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
    }
  }

  // Daten mutieren
}
'use server'

import { z } from 'zod'

const schema = z.object({
  email: z.string({
    invalid_type_error: 'Ungültige E-Mail',
  }),
})

export default async function createsUser(formData) {
  const validatedFields = schema.safeParse({
    email: formData.get('email'),
  })

  // Frühzeitig zurückkehren, wenn die Formulardaten ungültig sind
  if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
    }
  }

  // Daten mutieren
}

Sobald die Felder auf dem Server validiert wurden, können Sie ein serialisierbares Objekt in Ihrer Action zurückgeben und den React useFormState-Hook verwenden, um dem Benutzer eine Nachricht anzuzeigen.

  • Durch das Übergeben der Action an useFormState ändert sich die Funktionssignatur der Action, um einen neuen prevState- oder initialState-Parameter als erstes Argument zu erhalten.
  • useFormState ist ein React-Hook und muss daher in einer Client-Komponente verwendet werden.
'use server'

export async function createUser(prevState: any, formData: FormData) {
  // ...
  return {
    message: 'Bitte geben Sie eine gültige E-Mail ein',
  }
}
'use server'

export async function createUser(prevState, formData) {
  // ...
  return {
    message: 'Bitte geben Sie eine gültige E-Mail ein',
  }
}

Dann können Sie Ihre Action an den useFormState-Hook übergeben und den zurückgegebenen state verwenden, um eine Fehlermeldung anzuzeigen.

'use client'

import { useFormState } from 'react-dom'
import { createUser } from '@/app/actions'

const initialState = {
  message: '',
}

export function Signup() {
  const [state, formAction] = useFormState(createUser, initialState)

  return (
    <form action={formAction}>
      <label htmlFor="email">E-Mail</label>
      <input type="text" id="email" name="email" required />
      {/* ... */}
      <p aria-live="polite" className="sr-only">
        {state?.message}
      </p>
      <button>Registrieren</button>
    </form>
  )
}
'use client'

import { useFormState } from 'react-dom'
import { createUser } from '@/app/actions'

const initialState = {
  message: '',
}

export function Signup() {
  const [state, formAction] = useFormState(createUser, initialState)

  return (
    <form action={formAction}>
      <label htmlFor="email">E-Mail</label>
      <input type="text" id="email" name="email" required />
      {/* ... */}
      <p aria-live="polite" className="sr-only">
        {state?.message}
      </p>
      <button>Registrieren</button>
    </form>
  )
}

Gut zu wissen:

Optimistische Updates

Sie können den React useOptimistic-Hook verwenden, um die UI optimistisch zu aktualisieren, bevor die Server Action abgeschlossen ist, anstatt auf die Antwort zu warten:

'use client'

import { useOptimistic } from 'react'
import { send } from './actions'

type Message = {
  message: string
}

export function Thread({ messages }: { messages: Message[] }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic<
    Message[],
    string
  >(messages, (state, newMessage) => [...state, { message: newMessage }])

  return (
    <div>
      {optimisticMessages.map((m, k) => (
        <div key={k}>{m.message}</div>
      ))}
      <form
        action={async (formData: FormData) => {
          const message = formData.get('message')
          addOptimisticMessage(message)
          await send(message)
        }}
      >
        <input type="text" name="message" />
        <button type="submit">Senden</button>
      </form>
    </div>
  )
}
'use client'

import { useOptimistic } from 'react'
import { send } from './actions'

export function Thread({ messages }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [...state, { message: newMessage }]
  )

  return (
    <div>
      {optimisticMessages.map((m) => (
        <div>{m.message}</div>
      ))}
      <form
        action={async (formData) => {
          const message = formData.get('message')
          addOptimisticMessage(message)
          await send(message)
        }}
      >
        <input type="text" name="message" />
        <button type="submit">Senden</button>
      </form>
    </div>
  )
}

Verschachtelte Elemente

Sie können eine Server Action in Elementen aufrufen, die in <form> verschachtelt sind, wie <button>, <input type="submit"> und <input type="image">. Diese Elemente akzeptieren die formAction-Prop oder Event-Handler.

Dies ist nützlich, wenn Sie mehrere Server Actions innerhalb eines Formulars aufrufen möchten. Beispielsweise können Sie ein spezifisches <button>-Element zum Speichern eines Post-Entwurfs zusätzlich zum Veröffentlichen erstellen. Siehe die React <form>-Dokumentation für weitere Informationen.

Programmgesteuerte Formularübermittlung

Sie können eine Formularübermittlung mit der Methode requestSubmit() auslösen. Zum Beispiel können Sie auf das onKeyDown-Ereignis hören, wenn der Benutzer + Enter drückt:

'use client'

export function Entry() {
  const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if (
      (e.ctrlKey || e.metaKey) &&
      (e.key === 'Enter' || e.key === 'NumpadEnter')
    ) {
      e.preventDefault()
      e.currentTarget.form?.requestSubmit()
    }
  }

  return (
    <div>
      <textarea name="entry" rows={20} required onKeyDown={handleKeyDown} />
    </div>
  )
}
'use client'

export function Entry() {
  const handleKeyDown = (e) => {
    if (
      (e.ctrlKey || e.metaKey) &&
      (e.key === 'Enter' || e.key === 'NumpadEnter')
    ) {
      e.preventDefault()
      e.currentTarget.form?.requestSubmit()
    }
  }

  return (
    <div>
      <textarea name="entry" rows={20} required onKeyDown={handleKeyDown} />
    </div>
  )
}

Dies löst die Übermittlung des nächstgelegenen <form>-Vorfahrenelements aus, wodurch die Server-Aktion aufgerufen wird.

Nicht-Formular-Elemente

Während Server-Aktionen häufig innerhalb von <form>-Elementen verwendet werden, können sie auch aus anderen Teilen Ihres Codes wie Event-Handlern und useEffect aufgerufen werden.

Event-Handler

Sie können eine Server-Aktion aus Event-Handlern wie onClick aufrufen. Zum Beispiel, um einen Like-Zähler zu erhöhen:

'use client'

import { incrementLike } from './actions'
import { useState } from 'react'

export default function LikeButton({ initialLikes }: { initialLikes: number }) {
  const [likes, setLikes] = useState(initialLikes)

  return (
    <>
      <p>Total Likes: {likes}</p>
      <button
        onClick={async () => {
          const updatedLikes = await incrementLike()
          setLikes(updatedLikes)
        }}
      >
        Like
      </button>
    </>
  )
}
'use client'

import { incrementLike } from './actions'
import { useState } from 'react'

export default function LikeButton({ initialLikes }) {
  const [likes, setLikes] = useState(initialLikes)

  return (
    <>
      <p>Total Likes: {likes}</p>
      <button
        onClick={async () => {
          const updatedLikes = await incrementLike()
          setLikes(updatedLikes)
        }}
      >
        Like
      </button>
    </>
  )
}

Um die Benutzererfahrung zu verbessern, empfehlen wir die Verwendung anderer React-APIs wie useOptimistic und useTransition, um die Benutzeroberfläche zu aktualisieren, bevor die Server-Aktion auf dem Server abgeschlossen ist, oder um einen ausstehenden Zustand anzuzeigen.

Sie können auch Event-Handler zu Formularelementen hinzufügen, zum Beispiel, um ein Formularfeld onChange zu speichern:

app/ui/edit-post.tsx
'use client'

import { publishPost, saveDraft } from './actions'

export default function EditPost() {
  return (
    <form action={publishPost}>
      <textarea
        name="content"
        onChange={async (e) => {
          await saveDraft(e.target.value)
        }}
      />
      <button type="submit">Publish</button>
    </form>
  )
}

Für solche Fälle, in denen mehrere Ereignisse in schneller Folge ausgelöst werden können, empfehlen wir Debouncing, um unnötige Aufrufe von Server-Aktionen zu vermeiden.

useEffect

Sie können den React-Hook useEffect verwenden, um eine Server-Aktion aufzurufen, wenn die Komponente eingebunden wird oder eine Abhängigkeit sich ändert. Dies ist nützlich für Mutationen, die von globalen Ereignissen abhängen oder automatisch ausgelöst werden müssen. Zum Beispiel onKeyDown für App-Shortcuts, ein Intersection Observer-Hook für unendliches Scrollen oder wenn die Komponente eingebunden wird, um eine Aufrufzahl zu aktualisieren:

'use client'

import { incrementViews } from './actions'
import { useState, useEffect } from 'react'

export default function ViewCount({ initialViews }: { initialViews: number }) {
  const [views, setViews] = useState(initialViews)

  useEffect(() => {
    const updateViews = async () => {
      const updatedViews = await incrementViews()
      setViews(updatedViews)
    }

    updateViews()
  }, [])

  return <p>Total Views: {views}</p>
}
'use client'

import { incrementViews } from './actions'
import { useState, useEffect } from 'react'

export default function ViewCount({ initialViews }: { initialViews: number }) {
  const [views, setViews] = useState(initialViews)

  useEffect(() => {
    const updateViews = async () => {
      const updatedViews = await incrementViews()
      setViews(updatedViews)
    }

    updateViews()
  }, [])

  return <p>Total Views: {views}</p>
}

Denken Sie daran, die Verhaltensweisen und Einschränkungen von useEffect zu beachten.

Fehlerbehandlung

Wenn ein Fehler auftritt, wird er vom nächsten error.js oder <Suspense>-Boundary auf dem Client abgefangen. Wir empfehlen die Verwendung von try/catch, um Fehler zurückzugeben, die von Ihrer Benutzeroberfläche behandelt werden können.

Zum Beispiel könnte Ihre Server-Aktion Fehler bei der Erstellung eines neuen Elements behandeln, indem sie eine Nachricht zurückgibt:

'use server'

export async function createTodo(prevState: any, formData: FormData) {
  try {
    // Daten mutieren
  } catch (e) {
    throw new Error('Failed to create task')
  }
}
'use server'

export async function createTodo(prevState, formData) {
  try {
    // Daten mutieren
  } catch (e) {
    throw new Error('Failed to create task')
  }
}

Gut zu wissen:

Daten neu validieren

Sie können den Next.js Cache innerhalb Ihrer Server-Aktionen mit der revalidatePath-API neu validieren:

'use server'

import { revalidatePath } from 'next/cache'

export async function createPost() {
  try {
    // ...
  } catch (error) {
    // ...
  }

  revalidatePath('/posts')
}
'use server'

import { revalidatePath } from 'next/cache'

export async function createPost() {
  try {
    // ...
  } catch (error) {
    // ...
  }

  revalidatePath('/posts')
}

Oder Sie können einen bestimmten Datenabruf mit einem Cache-Tag über revalidateTag ungültig machen:

'use server'

import { revalidateTag } from 'next/cache'

export async function createPost() {
  try {
    // ...
  } catch (error) {
    // ...
  }

  revalidateTag('posts')
}
'use server'

import { revalidateTag } from 'next/cache'

export async function createPost() {
  try {
    // ...
  } catch (error) {
    // ...
  }

  revalidateTag('posts')
}

Umleitung

Wenn Sie den Benutzer nach Abschluss einer Server-Aktion zu einer anderen Route weiterleiten möchten, können Sie die redirect-API verwenden. redirect muss außerhalb des try/catch-Blocks aufgerufen werden:

'use server'

import { redirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'

export async function createPost(id: string) {
  try {
    // ...
  } catch (error) {
    // ...
  }

  revalidateTag('posts') // Cache für Beiträge aktualisieren
  redirect(`/post/${id}`) // Zur neuen Beitragsseite navigieren
}
'use server'

import { redirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'

export async function createPost(id) {
  try {
    // ...
  } catch (error) {
    // ...
  }

  revalidateTag('posts') // Cache für Beiträge aktualisieren
  redirect(`/post/${id}`) // Zur neuen Beitragsseite navigieren
}

Cookies

Sie können Cookies innerhalb einer Server-Aktion mit der cookies-API get, set und delete:

'use server'

import { cookies } from 'next/headers'

export async function exampleAction() {
  // Cookie abrufen
  const value = cookies().get('name')?.value

  // Cookie setzen
  cookies().set('name', 'Delba')

  // Cookie löschen
  cookies().delete('name')
}
'use server'

import { cookies } from 'next/headers'

export async function exampleAction() {
  // Cookie abrufen
  const value = cookies().get('name')?.value

  // Cookie setzen
  cookies().set('name', 'Delba')

  // Cookie löschen
  cookies().delete('name')
}

Siehe weitere Beispiele zum Löschen von Cookies aus Server-Aktionen.

Sicherheit

Authentifizierung und Autorisierung

Sie sollten Server-Aktionen wie öffentlich zugängliche API-Endpunkte behandeln und sicherstellen, dass der Benutzer berechtigt ist, die Aktion auszuführen. Zum Beispiel:

app/actions.ts
'use server'

import { auth } from './lib'

export function addItem() {
  const { user } = auth()
  if (!user) {
    throw new Error('You must be signed in to perform this action')
  }

  // ...
}

Closures und Verschlüsselung

Das Definieren einer Server-Aktion innerhalb einer Komponente erstellt einen Closure, in dem die Aktion Zugriff auf den Gültigkeitsbereich der äußeren Funktion hat. Zum Beispiel hat die publish-Aktion Zugriff auf die Variable publishVersion:

export default function Page() {
  const publishVersion = await getLatestVersion();

  async function publish(formData: FormData) {
    "use server";
    if (publishVersion !== await getLatestVersion()) {
      throw new Error('The version has changed since pressing publish');
    }
    ...
  }

  return <button action={publish}>Publish</button>;
}
export default function Page() {
  const publishVersion = await getLatestVersion();

  async function publish() {
    "use server";
    if (publishVersion !== await getLatestVersion()) {
      throw new Error('The version has changed since pressing publish');
    }
    ...
  }

  return <button action={publish}>Publish</button>;
}

Closures sind nützlich, wenn Sie eine Momentaufnahme von Daten (z.B. publishVersion) zum Zeitpunkt des Renderings erfassen müssen, um sie später beim Aufruf der Aktion verwenden zu können.

Damit dies funktioniert, werden die erfassten Variablen jedoch an den Client und zurück an den Server gesendet, wenn die Aktion aufgerufen wird. Um zu verhindern, dass sensible Daten dem Client preisgegeben werden, verschlüsselt Next.js die geschlossenen Variablen automatisch. Für jede Aktion wird bei jedem Build einer Next.js-Anwendung ein neuer privater Schlüssel generiert. Dies bedeutet, dass Aktionen nur für einen bestimmten Build aufgerufen werden können.

Gut zu wissen: Wir empfehlen nicht, sich allein auf die Verschlüsselung zu verlassen, um zu verhindern, dass sensible Werte dem Client preisgegeben werden. Stattdessen sollten Sie die React taint APIs verwenden, um proaktiv zu verhindern, dass bestimmte Daten an den Client gesendet werden.

Überschreiben von Verschlüsselungsschlüsseln (fortgeschritten)

Wenn Sie Ihre Next.js-Anwendung selbst auf mehreren Servern hosten, kann jeder Serverinstanz ein anderer Verschlüsselungsschlüssel zugewiesen werden, was zu potenziellen Inkonsistenzen führen kann.

Um dies zu vermeiden, können Sie den Verschlüsselungsschlüssel mit der Umgebungsvariable process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY überschreiben. Durch die Angabe dieser Variable wird sichergestellt, dass Ihre Verschlüsselungsschlüssel über Builds hinweg bestehen bleiben und alle Serverinstanzen denselben Schlüssel verwenden.

Dies ist ein fortgeschrittener Anwendungsfall, bei dem ein konsistentes Verschlüsselungsverhalten über mehrere Bereitstellungen hinweg für Ihre Anwendung entscheidend ist. Sie sollten Standard-Sicherheitspraktiken wie Schlüsselrotation und Signierung berücksichtigen.

Gut zu wissen: Next.js-Anwendungen, die auf Vercel bereitgestellt werden, handhaben dies automatisch.

Erlaubte Ursprünge (fortgeschritten)

Da Server-Aktionen in einem <form>-Element aufgerufen werden können, sind sie für CSRF-Angriffe anfällig.

Im Hintergrund verwenden Server-Aktionen die POST-Methode, und nur diese HTTP-Methode ist für ihren Aufruf erlaubt. Dies verhindert die meisten CSRF-Schwachstellen in modernen Browsern, insbesondere da SameSite-Cookies standardmäßig aktiviert sind.

Als zusätzlichen Schutz vergleichen Server-Aktionen in Next.js auch den Origin-Header mit dem Host-Header (oder X-Forwarded-Host). Wenn diese nicht übereinstimmen, wird die Anfrage abgebrochen. Mit anderen Worten: Server-Aktionen können nur auf demselben Host aufgerufen werden wie die Seite, die sie hostet.

Für große Anwendungen, die Reverse Proxies oder mehrschichtige Backend-Architekturen verwenden (bei denen der Server-API sich von der Produktionsdomäne unterscheidet), wird empfohlen, die Konfigurationsoption serverActions.allowedOrigins zu verwenden, um eine Liste sicherer Ursprünge anzugeben. Die Option akzeptiert ein Array von Strings.

next.config.js
/** @type {import('next').NextConfig} */
module.exports = {
  experimental: {
    serverActions: {
      allowedOrigins: ['my-proxy.com', '*.my-proxy.com'],
    },
  },
}

Erfahren Sie mehr über Sicherheit und Server-Aktionen.

Zusätzliche Ressourcen

Weitere Informationen zu Server-Aktionen finden Sie in den folgenden React-Dokumenten: