Server Management

WordPress REST API:
401 nach Nginx-Migration beheben

Patrick Chowanski
13.04.2026
Inhalt

Alles lief. Monatelang. Dann kam der Serverumzug von Apache auf Nginx – und plötzlich antwortete die WordPress REST API mit 401 Unauthorized auf Anfragen die vorher problemlos funktioniert haben. Gleicher Code, gleiche Credentials, gleiche WordPress-Installation. Anderer Webserver.

Das ist die Sorte Fehler die einen eine Weile beschäftigt, weil die offensichtliche Antwort – „irgendwas am Server ist falsch konfiguriert" – zwar stimmt, aber nicht auf eine Art die sofort sichtbar ist. Die debug.log schweigt dazu. WordPress selbst sieht keinen Fehler. Und wenn du nicht weißt wo du hinschaust, kannst du lange auf Konfigurationsdateien starren.

In diesem Artikel zeige ich dir den exakten Debugging-Weg der zu diesem Bug geführt hat, was technisch passiert und warum der Fix so simpel ist – sobald du weißt wonach du suchst.

Was auf Apache funktioniert hat und auf Nginx nicht tut

Das Setup: Eine externe Anwendung – in diesem Fall ein n8n-Workflow, könnte aber genauso eine Mobile App oder ein eigenes Script sein – ruft die WordPress REST API auf. Die Authentifizierung läuft über einen Authorization-Header, wie es die API erwartet. Auf Apache: sauber, 200, fertig. Nach dem Umzug auf Nginx: 401. Immer.

Die erste Reaktion ist meistens eine Runde durch die WordPress-Einstellungen – REST API aktiviert? Application Passwords korrekt? Benutzerrechte stimmen? Alles okay. Dann Nginx-Config – irgendein Rewrite der die Header wegschmeißt? Nichts Offensichtliches. Dann fängt das ratlose Googeln an.

Das Problem liegt nicht in WordPress. Es liegt nicht mal direkt in Nginx. Es liegt darin wie HTTP-Clients mit Redirects umgehen – und dass Apache und Nginx in einem entscheidenden Punkt unterschiedlich konfiguriert sind.

Der Redirect den niemand auf dem Schirm hat

Die meisten Domains sind so eingerichtet, dass domain.de auf www.domain.de weiterleitet – oder umgekehrt. Das ist Standard, gut für SEO, macht kaum jemand Gedanken drüber. Auf Apache passiert dieser Redirect über eine RewriteRule in der .htaccess, auf Nginx über einen server-Block mit return 301.

Was dabei mit dem Authorization-Header passiert, ist der Knackpunkt: HTTP-Clients – curl, n8n, dein Anwendungscode – folgen einem 301-Redirect und schicken dabei standardmäßig keinen Authorization-Header mit auf die neue URL. Die ursprüngliche Anfrage ging an meinshop.de, der Redirect schickt sie zu www.meinshop.de, und der Client denkt: andere URL, anderer Kontext, Credentials lasse ich lieber weg. Sicherheitsvorsichtsmaßnahme, könnte man sagen. Oder: konstruierte Fehlerquelle, je nach Tagesform.

Warum hat Apache das dann nicht gemacht? Gute Frage. Apache hat den Redirect oft unter der Haube anders behandelt – intern, ohne den Client überhaupt zu involvieren – oder war so konfiguriert dass er den Header beim Weiterleiten behalten hat. Nginx macht das nicht. Nginx schickt dem Client brav seine 301 zurück, der Client folgt brav ohne Authorization, und WordPress sagt brav: kein Zutritt.

Nett von allen Beteiligten.

Debugging-Weg: curl als Röntgengerät

Das Schöne an diesem Bug ist dass er sich mit zwei curl-Aufrufen vollständig beweisen lässt. Keine Log-Dateien wälzen, kein Plugin-Deaktivieren-Marathon – einfach curl mit dem -v-Flag, das dir den kompletten Request-Response-Zyklus aufmacht.

Schritt 1: Anfrage an die Domain ohne www.

curl -v -H "Authorization: Bearer dein-application-password" \
  https://meinshop.de/wp-json/wp/v2/posts

Was du in der Ausgabe siehst:

> GET /wp-json/wp/v2/posts HTTP/2
> Host: meinshop.de
> Authorization: Bearer dein-application-password
>
< HTTP/2 301
< location: https://www.meinshop.de/wp-json/wp/v2/posts

Da ist er. Der 301 nach www.meinshop.de. curl folgt dem Redirect per Default nicht automatisch – ohne das -L-Flag macht curl genau eine Anfrage und gibt dir das Ergebnis. Das was du hier siehst ist: die Anfrage kam mit Authorization an, hat aber nur eine Weiterleitungsanweisung zurückbekommen.

Schritt 2: Redirect folgen und schauen was passiert

curl -v -L -H "Authorization: Bearer dein-application-password" \
  https://meinshop.de/wp-json/wp/v2/posts

Das -L-Flag sagt curl: folge Redirects. Und jetzt siehst du das Problem schwarz auf weiß:

> GET /wp-json/wp/v2/posts HTTP/2
> Host: meinshop.de
> Authorization: Bearer dein-application-password
>
< HTTP/2 301
< location: https://www.meinshop.de/wp-json/wp/v2/posts

> GET /wp-json/wp/v2/posts HTTP/2
> Host: www.meinshop.de
>
< HTTP/2 401

Siehst du den Unterschied zwischen dem ersten und dem zweiten Request? Im ersten ist Authorization: Bearer ... dabei. Im zweiten fehlt er komplett. curl hat den Header beim Folgen des Redirects stillschweigend fallengelassen – genau so wie deine Anwendung das auch macht – und WordPress antwortet folgerichtig mit 401, weil es keine Credentials bekommt.

Schritt 3: Bestätigung – direkt auf www.

curl -v -H "Authorization: Bearer dein-application-password" \
  https://www.meinshop.de/wp-json/wp/v2/posts
> GET /wp-json/wp/v2/posts HTTP/2
> Host: www.meinshop.de
> Authorization: Bearer dein-application-password
>
< HTTP/2 200

200. Der Authorization-Header kommt an, WordPress ist zufrieden, die API antwortet. Das Problem sitzt nicht in Nginx, nicht in WordPress – es sitzt in der Base-URL die deine Anwendung verwendet.

Der Fix: Base-URL auf die Ziel-Domain setzen

Jetzt wo du weißt was passiert, ist der Fix offensichtlich: Deine Anwendung darf niemals die URL aufrufen die zu einem Redirect führt – sie muss direkt auf die URL gehen wo WordPress tatsächlich erreichbar ist.

Wenn deine Domain auf www.meinshop.de weiterleitet, dann ist https://www.meinshop.de/wp-json/ deine Base-URL. Nicht https://meinshop.de/wp-json/. Die Weiterleitungskonfiguration interessiert deinen API-Client nicht – er ruft eine URL auf, bekommt eine Antwort, und wenn die Antwort ein 301 ist, schmeckt ihm das Weiterleiten nicht besonders gut.

In einem PHP-Script:

$base_url = 'https://www.meinshop.de/wp-json/wp/v2/';

$response = wp_remote_get( $base_url . 'posts', [
    'headers' => [
        'Authorization' => 'Bearer ' . $application_password,
    ],
] );

In JavaScript/Node:

const BASE_URL = 'https://www.meinshop.de/wp-json/wp/v2';

const response = await fetch(`${BASE_URL}/posts`, {
  headers: {
    'Authorization': `Bearer ${applicationPassword}`,
  },
});

Kein Redirect, kein Authorization-Header der unterwegs verschwindet, kein 401. Fertig.

Warum Apache das nicht gemacht hat – und was das bedeutet

Das ist der Teil der interessant ist wenn du verstehen willst was da technisch passiert ist, statt nur den Fix anwenden zu wollen.

Apache hat den Redirect je nach Konfiguration oft intern aufgelöst, ohne dem Client einen 301 zurückzuschicken. Insbesondere wenn der Redirect über mod_rewrite in der .htaccess lief und WordPress selbst bereits auf der richtigen Domain lief, ist der Request manchmal gar nicht als echter HTTP-Redirect beim Client angekommen – Apache hat ihn intern verarbeitet und direkt weitergegeben. Der Authorization-Header blieb dabei erhalten weil er nie die Hand gewechselt hat.

Das ist technisch gesehen kein vorbildliches Verhalten von Apache – es verschleiert was wirklich passiert. Nginx macht es RFC-konformer: ein 301 ist ein 301, der Client bekommt ihn, der Client entscheidet was er damit macht. Dass HTTP-Clients dabei standardmäßig keine Credentials weiterleiten ist auch kein Nginx-Bug sondern absichtliches Verhalten – du willst nicht dass dein Browser deinen Authorization-Header auf eine unbekannte Domain weiterleitet, nur weil irgendein Server einen Redirect gesetzt hat. Macht Sinn. Hilft in diesem Moment trotzdem nicht.

Heißt konkret: Wenn du von Apache auf Nginx wechselst, werden Dinge sichtbar die vorher still unter der Haube funktioniert haben. Das ist manchmal anstrengend – es ist aber auch eine gute Gelegenheit Konfigurationsdinge aufzuräumen die schon immer so hätten aussehen sollen.

Was du außerdem prüfen solltest

Solange du schon dabei bist und curl offen hast, gibt es noch zwei Sachen die ich bei Nginx-Migrationen regelmäßig mache.

HTTP auf HTTPS – auch ein Redirect

Das gleiche Problem tritt auf wenn deine Anwendung noch http:// statt https:// verwendet und Nginx per 301 auf HTTPS weiterleitet. Gleiche Mechanik, gleiche Lösung: direkt https:// in der Base-URL verwenden. Prüfen geht so:

curl -v http://www.meinshop.de/wp-json/wp/v2/posts

Wenn du da einen 301 auf https:// bekommst: Base-URL in der Anwendung korrigieren.

Authorization-Header in der Nginx-Config

Manchmal – seltener, aber passiert – schmeißt die Nginx-Konfiguration den Authorization-Header aktiv raus, weil eine Direktive das explizit macht oder FastCGI ihn nicht weitergibt. Das siehst du daran dass der Request direkt auf www.meinshop.de ebenfalls einen 401 zurückgibt, wie in Schritt 3 oben. Dann ist es kein Redirect-Problem sondern ein Config-Problem.

Prüf in dem Fall ob in deinem PHP-FPM-Setup oder in der Nginx-Site-Config diese Zeile auftaucht:

fastcgi_param HTTP_AUTHORIZATION $http_authorization;

Wenn sie fehlt, füg sie hinzu. Wo genau – das hängt von deiner Nginx-Config-Struktur ab, meistens gehört das in den fastcgi_params-Block. Und wenn du dir dabei unsicher bist: lieber zweimal hinschauen als einmal zu viel in der Nginx-Config rumfummeln. Nginx verzeiht Konfigurationsfehler nicht besonders großzügig.

Zusammenfassung

Der 401 kam nicht von WordPress. Er kam nicht von Nginx. Er kam davon dass die API-Base-URL auf eine Domain zeigte die zu einem 301 führt – und HTTP-Clients den Authorization-Header beim Folgen eines Redirects standardmäßig nicht mitschicken. Apache hatte das jahrelang unter der Haube versteckt, Nginx legte es offen.

Der Debugging-Weg: curl -v auf die API-URL ohne www. zeigt den 301, curl -v -L auf die gleiche URL zeigt wie der Authorization-Header beim Redirect-Folgen verschwindet, curl -v direkt auf die Ziel-URL zeigt den 200.

Der Fix: Base-URL in der Anwendung direkt auf die Ziel-Domain setzen, auf der WordPress tatsächlich erreichbar ist. Kein Redirect, kein Problem.

Falls du beim Migrieren auf Nginx auch andere seltsame Sachen siehst – PHP-Fehler die vorher still blieben, Plugin-Verhalten das sich geändert hat – lohnt sich ein Blick in die [WordPress debug.log](/wordpress-debug-log-aktivieren-und-lesen/). Nicht weil Nginx-Probleme da drin stehen, sondern weil ein Serverumzug oft der Moment ist wo sich PHP-Fehler die lange ruhig vor sich hin gebrochen haben plötzlich bemerkbar machen.

Und wenn du sagst „Patrick, ich will mich mit Serverumzügen und API-Debugging einfach nicht rumschlagen“ – kein Problem, das kriegen wir hin. Ich patche, du chillst. Dafür bin ich da.

Häufige Fragen zum Nginx Authorization-Header Problem

Apache hat Redirects – besonders interne mod_rewrite-Regeln – oft ohne echten HTTP-Roundtrip zum Client aufgelöst, wodurch der Authorization-Header erhalten blieb. Nginx schickt dem Client einen richtigen 301 zurück, und dann verhält sich der Client RFC-konform und lässt die Credentials weg. Nginx ist hier nicht falsch – Apache hat das Problem eher verschleiert.

Bei curl geht das mit dem --location-trusted-Flag statt -L. Damit sendet curl den Authorization-Header auch nach einem Redirect mit. Für Produktiv-Code ist das aber keine empfehlenswerte Lösung – du willst nicht dass Credentials unkontrolliert auf Redirect-Ziele weitergegeben werden, besonders wenn das Redirect-Ziel mal wechselt. Besser direkt die richtige URL verwenden.

Dann ist der Redirect nicht das Problem. Prüf ob fastcgi_param HTTP_AUTHORIZATION $http_authorization; in deiner Nginx/FastCGI-Konfiguration gesetzt ist und ob WordPress Application Passwords auf deiner Installation aktiviert sind. Und check mit curl -v direkt auf die www-URL ob der Header tatsächlich ankommt.

Für alle Header-basierten Auth-Methoden – Application Passwords, OAuth-Token, Custom Authorization-Header. Alles was über den Authorization-Header geht wird beim Redirect-Folgen weggeschmissen. Cookie-basierte Authentifizierung verhält sich anders, betrifft die REST API in der Regel aber nicht.

In WordPress unter Einstellungen → Allgemein stehen „WordPress-Adresse“ und „Website-Adresse“. Die URL die dort steht – mit oder ohne www. – ist die URL auf die Nginx weiterleitet, und die sollte auch deine API-Base-URL sein.

Nein. Das gleiche Verhalten tritt bei jedem API-Aufruf auf der über einen Redirect läuft – egal ob REST API, GraphQL oder eigene Endpoints. Sogar normale Formular-Posts können davon betroffen sein. Der Redirect-Authorization-Mechanismus ist HTTP-Client-Verhalten, keine WordPress-Eigenheit.

Artikel teilen