# How to Deploy MetaMCP Behind Nginx: Ensuring SSE Compatibility

> Deploy MetaMCP behind Nginx and ensure SSE compatibility. Learn to configure Nginx for uninterrupted Server-Sent Events streaming by disabling buffering and managing connection headers.

- Repository: [metatool-ai/metamcp](https://github.com/metatool-ai/metamcp)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Configure Nginx with `keepalive_timeout 300`, disable buffering via `X-Accel-Buffering 'no'`, and clear the Connection header to stream MetaMCP's Server-Sent Events without interruption.**

MetaMCP relies on **Server-Sent Events (SSE)** to stream real-time data through long-lived HTTP connections, which requires specific reverse-proxy tuning to avoid premature connection drops. When deploying the `metatool-ai/metamcp` repository behind Nginx, default buffering settings will block or delay the event stream, breaking the real-time experience for downstream clients. This guide provides the exact configuration directives and source file references needed to deploy MetaMCP behind Nginx while maintaining full SSE compatibility.

## Why SSE Requires Special Nginx Configuration

MetaMCP uses SSE to maintain persistent connections between the backend and clients such as Open WebUI or Claude Desktop. In [`apps/backend/src/routers/public-metamcp/sse.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/routers/public-metamcp/sse.ts), the application handles the `/metamcp/:endpoint_name/sse` route by streaming `EventSource` data over a single HTTP connection that can remain open for minutes or hours. Nginx's default behavior—buffering responses and closing idle connections after 75 seconds—directly conflicts with this architecture.

To support SSE properly, you must:
- **Extend connection timeouts** to prevent Nginx from closing idle streams
- **Disable response buffering** to ensure immediate delivery of each `data:` line
- **Preserve original request headers** so MetaMCP can correctly build URLs and perform authentication

## Critical Nginx Directives for MetaMCP SSE

The repository provides a working reference in `nginx.conf.example`. The following directives are essential for SSE compatibility:

| Directive | Purpose | Source Location |
|-----------|---------|-----------------|
| `keepalive_timeout 300;` | Extends idle timeout from the default 75s to 5 minutes (or longer) to support long-lived streams | [`nginx.conf.example`, line 31](https://github.com/metatool-ai/metamcp/blob/main/nginx.conf.example#L31) |
| `proxy_set_header Connection '';` | Removes the `Connection: close` header that would otherwise terminate the stream immediately | [`nginx.conf.example`, line 134](https://github.com/metatool-ai/metamcp/blob/main/nginx.conf.example#L134) |
| `proxy_set_header Cache-Control 'no-cache';` | Prevents intermediate caches from buffering the event stream | [`nginx.conf.example`, line 138](https://github.com/metatool-ai/metamcp/blob/main/nginx.conf.example#L138) |
| `proxy_set_header X-Accel-Buffering 'no';` | Disables Nginx's internal response buffering, ensuring real-time delivery | [`nginx.conf.example`, line 139](https://github.com/metatool-ai/metamcp/blob/main/nginx.conf.example#L139) |
| `proxy_set_header Host $host;` | Preserves the original Host header for correct URL generation | [`nginx.conf.example`, lines 122-125](https://github.com/metatool-ai/metamcp/blob/main/nginx.conf.example#L122) |

## Complete Nginx Configuration for MetaMCP

Copy the following `location` block into your [`nginx.conf`](https://github.com/metatool-ai/metamcp/blob/main/nginx.conf), adjusting the `proxy_pass` URL to match your MetaMCP backend address (default port `12008`):

```nginx
http {
    # Critical for long-lived SSE connections

    keepalive_timeout 300;

    server {
        listen 8080;
        server_name localhost;

        # Static frontend assets

        location / {
            root html;
            index index.html index.htm;
        }

        # MetaMCP SSE and API proxy

        location /metamcp/ {
            proxy_pass http://localhost:12008;
            proxy_http_version 1.1;
            
            # Preserve client information

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            
            # SSE-specific headers (disable buffering)

            proxy_set_header Connection '';
            proxy_set_header Cache-Control 'no-cache';
            proxy_set_header X-Accel-Buffering 'no';
        }
    }
}

```

**Key configuration details:**
- **`proxy_http_version 1.1`** – Required for keepalive connections to the backend
- **`proxy_pass http://localhost:12008`** – Points to the MetaMCP backend container or process
- **Location path `/metamcp/`** – Matches the route defined in [`apps/backend/src/routers/public-metamcp/sse.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/routers/public-metamcp/sse.ts)

## Docker Compose Integration

If running MetaMCP with Docker Compose, expose the backend port and mount your custom Nginx configuration:

```yaml
services:
  metamcp-backend:
    build: .
    container_name: metamcp-backend
    expose:
      - "12008"
    environment:
      - NODE_ENV=production

  nginx:
    image: nginx:stable-alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "8080:8080"
    depends_on:
      - metamcp-backend

```

## Verifying SSE Compatibility Through Nginx

Test that Nginx correctly proxies the SSE stream without buffering by connecting directly to the `/metamcp/:endpoint_name/sse` endpoint:

```bash
curl -N -H "Accept: text/event-stream" http://localhost:8080/metamcp/my-endpoint/sse

```

The `-N` flag disables curl's buffering, allowing you to observe each `data:` line as the server emits it. If the configuration is correct, you will see a continuous stream of events; if buffering is misconfigured, the connection will hang or timeout without displaying data.

This verification confirms that the directives from `nginx.conf.example` are functioning correctly and that your MetaMCP instance can stream events reliably through the reverse proxy.

## Summary

- **Extend timeouts**: Set `keepalive_timeout 300` (or higher) in the `http` block to prevent Nginx from closing idle SSE connections
- **Disable buffering**: Use `proxy_set_header X-Accel-Buffering 'no'` and `proxy_set_header Connection ''` to ensure real-time event delivery
- **Preserve headers**: Pass `Host`, `X-Real-IP`, and forwarding headers so MetaMCP can authenticate requests and generate correct URLs
- **Reference implementation**: Consult [`apps/backend/src/routers/public-metamcp/sse.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/routers/public-metamcp/sse.ts) for the SSE endpoint logic and `nginx.conf.example` for the complete working configuration
- **Test with curl**: Use `curl -N` to verify unbuffered streaming through your Nginx proxy before deploying to production

## Frequently Asked Questions

### What port does the MetaMCP backend run on by default?

The MetaMCP backend listens on port **12008** by default, as reflected in the `proxy_pass` directive within `nginx.conf.example`. When deploying with Docker, expose this port internally and map Nginx to communicate with `http://metamcp-backend:12008` (or `localhost:12008` for single-host deployments).

### Why does SSE stop working when I add Nginx?

Nginx buffers responses by default to optimize throughput, which breaks SSE's real-time nature. Additionally, Nginx closes idle connections after 75 seconds by default. To fix this, you must disable buffering with `X-Accel-Buffering 'no'`, clear the `Connection` header, and increase `keepalive_timeout` as shown in the configuration above.

### Can I use this same configuration for the internal MCP proxy routes?

Yes. The SSE transport is also used for internal proxy routes implemented in [`apps/backend/src/routers/mcp-proxy/server.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/routers/mcp-proxy/server.ts). The same Nginx location block and buffering directives apply to any MetaMCP endpoint that utilizes Server-Sent Events, ensuring consistent behavior across public and internal APIs.

### How do I handle authentication headers when proxying through Nginx?

Pass the original `Host` header and client IP information using `proxy_set_header Host $host`, `X-Real-IP $remote_addr`, and `X-Forwarded-For $proxy_add_x_forwarded_for`. MetaMCP inspects these headers to validate requests and build absolute URLs, so preserving them is essential for correct authentication and routing behavior.