How to Fix CORS Issues When Deploying Prompt-Optimizer as a Web App

Configure the Express MCP server with the cors middleware to explicitly allow your web app's origin, or use Nginx reverse proxy headers to handle cross-origin requests at the edge.

When deploying the linshenkx/prompt-optimizer repository as a production web application, Cross-Origin Resource Sharing (CORS) errors frequently block API calls between the Vite frontend and the Express-based MCP server. These issues typically manifest when the packages/web frontend attempts to communicate with the packages/mcp-server backend across different origins, ports, or protocols.

Understanding CORS in the Prompt-Optimizer Architecture

The Separation of Frontend and Backend

The Prompt-Optimizer project is organized as a monorepo where packages/web contains the Vue.js frontend built with Vite, while packages/mcp-server houses the Express API that handles model context protocol operations. When you deploy these services separately—serving the web app from https://app.example.com and the API from https://api.example.com—the browser enforces CORS policies that require explicit permission from the server to complete cross-origin requests.

Common CORS Issues When Deploying Prompt-Optimizer

Missing or Mismatched Access-Control-Allow-Origin Headers

The most frequent error occurs when the MCP server returns responses without the Access-Control-Allow-Origin header, or returns a specific origin that doesn't match the browser's request. In packages/mcp-server/src/index.ts, if the Express app doesn't configure CORS middleware, browsers will block all API responses with errors like "CORS header 'Access-Control-Allow-Origin' missing."

Credentials and Wildcard Conflicts

When the frontend in packages/web sends requests with credentials: 'include' to pass authentication cookies or tokens, the server must respond with Access-Control-Allow-Credentials: true. However, CORS specifications forbid using the wildcard * for Access-Control-Allow-Origin when credentials are enabled. This creates a conflict if the Express server is configured with origin: '*' while credentials: true is also set.

Pre-flight OPTIONS Request Failures

Complex requests—such as POST calls with Content-Type: application/json or custom headers like Authorization—trigger pre-flight OPTIONS requests. If the MCP server doesn't handle these OPTIONS requests properly in packages/mcp-server, the browser never sends the actual API call. This typically happens when the server lacks explicit handling for the OPTIONS method or doesn't return the required Access-Control-Allow-Methods and Access-Control-Allow-Headers headers.

Protocol and Port Mismatches

Deploying the web app over HTTPS while the API remains on HTTP creates mixed-content blocks that appear similar to CORS errors. Similarly, using different ports in development—such as localhost:5173 for the Vite dev server and localhost:4000 for the MCP server—triggers origin mismatches unless properly configured.

Resolving CORS Issues in Prompt-Optimizer

Configure CORS in the Express MCP Server

The most direct solution involves adding the cors middleware to the Express application in packages/mcp-server. Install the package and configure it to allow your specific web app origin:

// File: packages/mcp-server/src/index.ts
import express from 'express';
import cors from 'cors';
import routes from './routes';

const app = express();

app.use(
  cors({
    origin: process.env.CORS_ORIGIN || '*', // replace '*' with your domain in prod
    credentials: true,
  })
);

app.use(express.json());
app.use('/api', routes);
export default app;

For production deployments, replace the wildcard * with the exact URL of your deployed web app (e.g., https://prompt-optimizer.example.com) and ensure credentials: true is only enabled when necessary.

Set Up Nginx Reverse Proxy Headers

If you deploy Prompt-Optimizer using the included Docker setup with Nginx, you can handle CORS at the reverse proxy level in docker/nginx.conf. This approach centralizes CORS handling and keeps application code clean:


# File: docker/nginx.conf (inside the server block)

location /api/ {
    proxy_pass http://mcp-server:4000/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;

    # CORS headers

    add_header Access-Control-Allow-Origin $http_origin always;
    add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS' always;
    add_header Access-Control-Allow-Headers 'Authorization,Content-Type' always;
    add_header Access-Control-Allow-Credentials 'true' always;

    # Handle pre‑flight

    if ($request_method = OPTIONS) {
        return 204;
    }
}

This configuration dynamically reads the requesting origin via $http_origin, allowing multiple domains while supporting credentials. The OPTIONS handler returns HTTP 204 for pre-flight requests without forwarding them to the backend.

Use Vite Dev Server Proxy for Local Development

During development, you can bypass CORS entirely by configuring the Vite dev server in packages/web/vite.config.ts to proxy API requests to the MCP server:

// File: packages/web/vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:4000',
        changeOrigin: true,
        secure: false,
      },
    },
  },
});

This setup makes the browser believe requests to /api originate from the same origin as the Vite dev server (typically localhost:5173), eliminating CORS errors during local testing. Note that this proxy only applies to the development server and does not affect production builds.

Summary

  • CORS errors in Prompt-Optimizer occur when the packages/web frontend attempts to call the packages/mcp-server API across different origins, ports, or protocols.
  • Missing headers like Access-Control-Allow-Origin or improper handling of pre-flight OPTIONS requests are the most common root causes.
  • Resolution methods include adding the cors middleware to packages/mcp-server/src/index.ts, configuring docker/nginx.conf for reverse proxy header injection, or using the Vite proxy in packages/web/vite.config.ts for development.
  • Production deployments should specify exact allowed origins rather than wildcards when credentials are enabled, and ensure HTTPS protocols match between frontend and backend.

Frequently Asked Questions

Why do I get CORS errors only in production but not during local development?

Local development typically runs both the Vite frontend and Express backend on localhost with different ports, but browsers treat different ports as different origins. However, if you're using the Vite dev server proxy configured in packages/web/vite.config.ts, the browser sees all API requests as same-origin, masking CORS issues. In production, the frontend and backend usually reside on separate domains or subdomains without a proxy, exposing any missing CORS headers from the MCP server.

Can I use wildcards for CORS origins when deploying Prompt-Optimizer?

You can use origin: '*' in the Express CORS configuration for development or public APIs, but this approach fails when your frontend sends requests with credentials: 'include' to pass authentication tokens or cookies. The CORS specification forbids combining wildcards with credentials. For production deployments of Prompt-Optimizer, specify the exact origin of your web app (e.g., https://optimizer.example.com) in the CORS_ORIGIN environment variable or Nginx configuration.

How do I handle CORS for the MCP server API endpoints?

The MCP server in packages/mcp-server uses Express, so you should install the cors middleware (npm install cors) and apply it in the main entry file (src/index.ts). Configure it to allow your web app's origin and handle pre-flight requests automatically. Alternatively, if you deploy behind the provided Nginx Docker setup, configure CORS headers in docker/nginx.conf to handle cross-origin requests at the reverse proxy layer before they reach the MCP server.

Is it better to handle CORS in Nginx or in the Express application?

Handling CORS in Nginx via docker/nginx.conf is generally preferred for production deployments because it centralizes security policy, reduces load on the Express application, and handles pre-flight requests efficiently without hitting your backend server. However, configuring CORS directly in the Express app (packages/mcp-server) offers more granular control per route and easier debugging during development. Many deployments use both: strict origin validation in Express and broad header handling in Nginx for defense in depth.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →