Fuckup der Woche: Wie ein unscheinbarer Origin-Header unsere Proxy-Konfiguration sabotierte
In der Kategorie Fuckup der Woche geht es heute um ein Beispiel, das zeigt, wie viel Zeit man in etwas scheinbar Simples wie eine Proxy-Konfiguration investieren kann – und warum das am Ende lehrreich war.
Problem: Ein Proxy für eine API sollte lokale Anfragen weiterleiten, und dabei traten wiederholt 403 Forbidden-Fehler auf. Die Ursache? Ein unscheinbarer Origin-Header, den wir (fast) übersehen hätten.

Der Plan: Ein Proxy, der mit Zertifikaten und CORS umgehen kann
Unser Setup basierte auf Vite, einem beliebten Build-Tool für moderne Frontend-Entwicklung. Die Herausforderung bestand darin, einen Proxy zu konfigurieren, der Anfragen von einem lokalen Frontend an einen entfernten Server (TARGET_URL) weiterleitet, ohne an selbstsignierten SSL-Zertifikaten oder Cross-Origin Resource Sharing (CORS)-Problemen zu scheitern. Der Proxy sollte:
- SSL-Zertifikat-Fehler ignorieren: Lokale Entwicklung bringt häufig selbstsignierte Zertifikate mit sich, also wollten wir
secure: falsesetzen, um Zertifikatsfehler zu umgehen. - CORS-Anfragen zulassen: Der Proxy sollte die CORS-Vorgaben für Preflight-Anfragen (
OPTIONS) unterstützen und die richtigen Header setzen. - Zieladresse umschreiben: Nur die Anfragen an den API-Pfad
^/centerdevice-rest-server/v2/ai-searchsollten umgeleitet und die Basispfade korrekt angepasst werden.
Der Weg dorthin: 403 Forbidden und die Suche nach dem Übeltäter
Mit unserer Proxy-Konfiguration standen wir am Anfang vor einem ständigen 403 Forbidden-Fehler. Trotz gefühlt endloser Anpassungen von changeOrigin, secure, und rewrite, konnte der Proxy die Anfrage einfach nicht durchbringen. Der Server lehnte die Anfrage weiterhin ab, als ob wir an ein unsichtbares Hindernis stießen.
Was wir ausprobiert haben:
- CORS-Header setzen: Zuerst schien es logisch, dass der
403-Fehler durch fehlende CORS-Header verursacht wurde. Also ergänzten wirAccess-Control-Allow-Origin,Access-Control-Allow-Methods, undAccess-Control-Allow-Headers. - Preflight-Anfragen beantworten: Wir fügten eine
OPTIONS-Antwort hinzu, um den Browser zufriedenzustellen, der jedes Mal mit einem Preflight-Test das Feld abfragte. - changeOrigin: Wir schalteten
changeOrigin: true, damit der Proxy alle Anfragen so weiterleitete, als ob sie vom Ziel-Host kämen. Doch der Server blieb stur.
Die Lösung: Den Origin-Header entfernen
Nach einer ausgiebigen Inspektion von curl-Befehlen und einer genauen Überprüfung der Proxy-Req-Header fiel uns ein unscheinbarer Verdächtiger auf: der Origin-Header. Anders als curl, das ohne diesen Header arbeitet, schickte der Proxy ihn hartnäckig mit. Das Problem: Der entfernte Server akzeptierte die Anfragen nur, wenn der Origin-Header weggelassen wurde. Eine Regel, die in der API-Dokumentation nicht erwähnt war und die nur durch Ausschlussverfahren herauszufinden war.
Also: proxyReq.removeHeader("origin");
Sobald wir den Origin-Header entfernt hatten, war der Weg frei. Kein 403 mehr. Die Anfrage ging durch, als wäre nie etwas gewesen.
Der finale Code
Hier ist der endgültige Proxy-Setup in vite.config.js, der das Problem umgeht, indem der Origin-Header explizit entfernt wird und alle notwendigen CORS- und Fehler-Handler berücksichtigt:
import { defineConfig } from "vite";
import dotenv from "dotenv";
dotenv.config();
export default defineConfig({
server: {
port: process.env.PORT || 5000,
proxy: {
"^/derkuba-rest-server/v2/ai-search": {
target: process.env.TARGET_URL,
changeOrigin: true,
secure: false,
rewrite: (path) =>
path.replace(/^\/derkuba-rest-server\/v2\/ai-search/, ""),
configure: (proxy) => {
// Setzt CORS-Header für jede Anfrage
proxy.on("proxyReq", (proxyReq, req, res) => {
proxyReq.removeHeader("origin"); // Entfernt den Origin-Header
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS",
);
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization",
);
console.log(
`[Incoming Request] ${req.method} ${req.url}`,
);
console.log(
`[Outgoing Request] ${proxyReq.method} ${proxyReq.getHeader("host")}${proxyReq.path}`,
);
});
// Preflight-Anfragen für CORS beantworten
proxy.on("proxyRes", (proxyRes, req, res) => {
if (req.method === "OPTIONS") {
res.writeHead(200, {
"Content-Type": "text/plain",
});
res.end();
}
});
// Fehler-Handler mit CORS und Content-Type
proxy.on("error", (err, req, res) => {
console.error(`Proxy error: ${err.message}`);
if (res && !res.headersSent) {
res.writeHead(500, {
"Content-Type": "text/plain",
"Access-Control-Allow-Origin": "*",
});
res.end("Something went wrong with the proxy.");
}
});
},
},
},
},
});Fazit
Der Origin-Header mag unscheinbar sein, kann aber bei serverseitigen API-Beschränkungen den Unterschied zwischen Erfolg und einem 403 Forbidden-Fehler machen. Der heutige Fuckup der Woche ist eine Erinnerung daran, dass die kleinen Details oft die entscheidenden sind. Manchmal liegt die Lösung in der genauen Untersuchung des Request-Formats und in der geduldigen Fehlersuche – und nicht immer in großen Codeänderungen.
Für Feedback bin ich immer dankbar. Gerne an jacob@derkuba.de
Viele Grüße
Euer Kuba