---
title: "Nginx, VPS, proxy and CDN: deploy Éclair"
description: "Deploy Éclair on a VPS: a commented Nginx configuration, PHP-FPM, HTTPS with Certbot, reverse proxy, real IP, Cloudflare and a php:8.3-apache Docker example."
url: "https://eclair.academy/en/paths/expert/nginx-vps"
lang: "en"
author: "Thierry Gustin"
published: "2026-11-02"
updated: "2026-11-02"
---

# Nginx, VPS, proxy and CDN: deploy Éclair

> **Key takeaways** — To deploy Éclair outside shared hosting, reproduce the .htaccess rules in Nginx, send everything else to index.php through PHP-FPM with fastcgi_buffering off, turn on HTTPS with Certbot, then tell Éclair in Settings › Advanced whether the site sits behind a proxy or a CDN. A php:8.3-apache container with mod_rewrite and AllowOverride All also works.

To deploy Éclair on a VPS, behind a proxy or a CDN, you reproduce what `.htaccess` does on shared hosting: protect the internal folders, send everything to `index.php`, and compress and cache static files. This lesson gives you a commented Nginx configuration, the HTTPS, proxy and Cloudflare settings, a Docker example and the final checks.

> **Before you start** — Time: 1 hour. Prerequisites: a Debian or Ubuntu VPS with root access, Nginx and PHP-FPM 8.3 installed, a domain pointing to the server, and the [Éclair's architecture](https://eclair.academy/en/paths/expert/architecture) lesson. On shared hosting, you need none of this: see [Choosing your hosting](https://eclair.academy/en/hosting).

## What you'll be able to do

### 🔧 Configure Nginx

The .htaccess rules, PHP-FPM and the assistant's stream.

### 🔒 Switch to HTTPS

Certbot, Force HTTPS, the official address and HSTS.

### 🌍 Add a proxy or a CDN

Real IP address, Cloudflare and a Docker container.

## What Nginx must reproduce from .htaccess

Nginx ignores `.htaccess` files. Its configuration must therefore guarantee the same protections:

| .htaccess rule | Why |
| --- | --- |
| `app/`, `data/` and `cache/` return 404 | code, data and generated pages stay private |
| hidden files return 404, except `.well-known` | `.git` or `.env` stay invisible, the MCP connector's OAuth discovery stays reachable |
| no executable `.php` file other than `index.php` | a script dropped elsewhere never runs |
| `media/` without scripts or pages | a booby-trapped image doesn't become a page |
| everything else goes to `index.php` | Éclair's router responds, clean URLs included |
| compression, one-year browser caching | light pages, static files reused |

The `Authorization` header, which `.htaccess` has to copy for the MCP connector, is passed to PHP-FPM by Nginx automatically.

## The commented Nginx configuration

The rules follow the Nginx section of Éclair's official guide; the lines marked “added” complete the example for a real server. `root` is the folder that receives the contents of the archive's `eclair` folder.

```nginx
server {
    listen 80;
    listen [::]:80;
    server_name exemple.fr www.exemple.fr;
    root /var/www/eclair;
    index index.php;

    client_max_body_size 24m;            # added: images up to 20 MB (Nginx default: 1 MB)
    gzip on;                             # added: compression, like .htaccess
    gzip_types text/plain text/css text/xml text/markdown application/javascript
               application/json application/xml application/rss+xml image/svg+xml;

    # Internal folders and hidden files, except .well-known (OAuth discovery)
    location ~ ^/(app|data|cache)(/|$) { return 404; }
    location ~ /\.(?!well-known/) { return 404; }

    # Media: images and fonts only
    location ^~ /media/ {
        location ~* \.(php\d?|phtml|phar|s?html?|svgz?|xml|js)$ { return 404; }
        add_header X-Content-Type-Options nosniff;
        add_header Content-Security-Policy "default-src 'none'; img-src 'self'; sandbox";  # added, like media/.htaccess
        expires 1y;
    }

    # Versioned static files: one-year browser caching
    location ~* \.(webp|avif|jpe?g|png|gif|ico|css|js|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; }

    # Everything else goes through the front controller
    location / { try_files $uri /index.php$is_args$args; }

    location = /index.php {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
        fastcgi_pass unix:/run/php/php-fpm.sock;   # adapt, for example /run/php/php8.3-fpm.sock
        fastcgi_buffering off;                     # recommended by the guide: AI assistant streaming
        fastcgi_read_timeout 300s;                 # added: long AI requests (60 s by default)
    }

    # No other PHP script
    location ~ \.php$ { return 404; }
}
```

Three settings deserve an explanation:

- **`client_max_body_size`**: by default, Nginx refuses requests over 1 MB (error 413), whereas Éclair accepts 20 MB images. Also set `upload_max_filesize` and `post_max_size` in PHP-FPM's `php.ini`.
- **`fastcgi_buffering off`**: the AI assistant streams its replies. Éclair already sends `X-Accel-Buffering: no`, which Nginx respects by default; the directive covers configurations that ignore it.
- **`fastcgi_read_timeout`**: “Create with AI” replies in one go after 20 to 60 seconds, and Éclair allows itself up to 300 seconds, while Nginx cuts the connection after 60 seconds of silence.

```bash
sudo nginx -t && sudo systemctl reload nginx
# Éclair only writes to data/, cache/ and media/
sudo chown -R www-data:www-data /var/www/eclair/data /var/www/eclair/cache /var/www/eclair/media
```

## HTTPS with Certbot, then Éclair's settings

```bash
sudo certbot --nginx -d exemple.fr -d www.exemple.fr
```

Certbot obtains a Let's Encrypt certificate, adds a listener on port 443 to the `server` block and can redirect HTTP to HTTPS. Then install Éclair by opening `https://exemple.fr`: the wizard saves the address exactly as you opened it, and already checks **Force HTTPS**. Next, in the admin:

1. Open **Security**: the site address must be verified. If it isn't, click **Verify the site address**.
2. In **Settings › Advanced**, make sure **Force HTTPS** is checked. Éclair refuses to turn it on as long as you're not connected over https.
3. Leave **Redirect to the official URL** checked: a single version of the site, with or without www, as soon as the address is verified.
4. Once everything is stable, check **HSTS**. Éclair then sends `Strict-Transport-Security: max-age=31536000`, one year, without subdomains. Browsers will then refuse http:// for your domain, so never turn it on during tests.

Don't add HSTS in Nginx as well, or the header would be sent twice. To manage it in Nginx, uncheck Éclair's option, and remember that an `add_header` placed in a `location` block hides those of the `server` block.

## How do you deploy Éclair behind a reverse proxy?

When another server terminates HTTPS (Nginx in front of a container, Caddy, Traefik…), PHP receives HTTP. Éclair considers the connection secure if the `HTTPS` variable is set, if the port is 443, or if the request carries `X-Forwarded-Proto: https` or `X-Forwarded-SSL: on`. The proxy must therefore send `X-Forwarded-Proto` and keep the `Host` header, which Éclair compares with its official address.

```nginx
# Nginx as a reverse proxy in front of Éclair (container published on 127.0.0.1:8080)
location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;                  # original host, compared with the site address
    proxy_set_header X-Forwarded-Proto $scheme;   # https, as the visitor sees it
    proxy_set_header X-Real-IP $remote_addr;      # the visitor's real IP address
    proxy_buffering off;                          # AI assistant streaming
    proxy_read_timeout 300s;
    client_max_body_size 24m;
}
```

> **Don't publish the internal port** — Éclair trusts `X-Forwarded-Proto` wherever it comes from. Publish Éclair's port on `127.0.0.1` only, so that only your proxy can talk to it.

Behind a proxy, PHP sees the proxy's IP address: the brute-force limits and the form limits would hit every visitor at once. In **Settings › Advanced › Site behind a proxy / CDN**, choose the header that carries the real address. Éclair only reads it if you've chosen it, and keeps only the last address in `X-Forwarded-For`, the one added by the nearest proxy.

| Situation | Choice in Éclair |
| --- | --- |
| no proxy, or address already corrected by Nginx's realip module | No (recommended) |
| Nginx as a reverse proxy with `proxy_set_header X-Real-IP` | X-Real-IP |
| a proxy that appends to `X-Forwarded-For` | X-Forwarded-For |
| Cloudflare in front of the site, with or without a proxy behind it | Cloudflare |

Only choose a header if the site really is behind that service: otherwise, a visitor could set it themselves and get around the limits. Behind Cloudflare, only accept connections coming from the [IP addresses published by Cloudflare](https://www.cloudflare.com/ips/).

## Cloudflare: what to configure

- **Encryption**: “Full (strict)” SSL/TLS mode, with a real certificate on your server, such as the one from Certbot.
- **Firewall and bot protection**: let `/_eclair/` and `/.well-known/` through, otherwise Claude and ChatGPT can reach neither the MCP connector nor its authorization.
- **Cache**: static files are already marked for one year. Don't cache the HTML at Cloudflare: Éclair clears its own cache on every publication, not Cloudflare's, and neither the admin nor `/_eclair/` must ever end up there.
- **Options that inject scripts**: according to [Cloudflare's documentation](https://developers.cloudflare.com/fundamentals/reference/policies-compliances/content-security-policies/), Rocket Loader requires `ajax.cloudflare.com` in `script-src`, and Web Analytics requires `static.cloudflareinsights.com`. Éclair's CSP only allows scripts from the domain, so leave these options off.
- **IP address**: choose “Cloudflare” in **Settings › Advanced › Site behind a proxy / CDN**.

## How do you deploy Éclair with Docker?

Here is a starting example to adapt, based on the official `php:8.3-apache` image. It keeps Apache so that Éclair's `.htaccess` applies as is. Place this `Dockerfile` next to the unzipped `eclair` folder.

```dockerfile
# Starting example, to adapt: Éclair 1.8 on the official PHP 8.3 + Apache image
FROM php:8.3-apache

# Already in the image: mbstring, json, dom, session, zlib, curl, openssl, sodium, Argon2id.
# To add: gd with JPEG and WebP (images), zip (backups), exif (phone photos turned upright).
RUN apt-get update \
 && apt-get install -y --no-install-recommends libjpeg62-turbo-dev libpng-dev libwebp-dev libzip-dev \
 && docker-php-ext-configure gd --with-jpeg --with-webp \
 && docker-php-ext-install -j"$(nproc)" gd zip exif \
 && rm -rf /var/lib/apt/lists/*

# Modules used by .htaccess: rewriting, headers, compression (no effect if already enabled)
RUN a2enmod rewrite headers deflate

# .htaccess files allowed: the image already does this for /var/www/ (docker-php.conf), we make it explicit
RUN printf '%s\n' '<Directory /var/www/html>' '    AllowOverride All' '</Directory>' \
      > /etc/apache2/conf-available/eclair.conf \
 && a2enconf eclair

# Production PHP settings, image uploads up to 20 MB
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
 && printf '%s\n' 'upload_max_filesize = 20M' 'post_max_size = 24M' > "$PHP_INI_DIR/conf.d/eclair.ini"

# The contents of the archive's eclair folder, hidden files included
COPY --chown=www-data:www-data eclair/ /var/www/html/
```

```yaml
# compose.yaml: persistent data and media, port reserved for the machine's HTTPS proxy
services:
  eclair:
    build: .
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - eclair-data:/var/www/html/data
      - eclair-media:/var/www/html/media
volumes:
  eclair-data:
  eclair-media:
```

`docker compose up -d --build` starts the site on the machine's port 8080, behind the HTTPS proxy from the previous section. The named volumes keep `data/` and `media/` from one version to the next: to update, replace the `eclair` folder with the one from the new archive and rebuild the image. The official image also enables OPcache by default (Dockerfile checked on November 1, 2026).

```bash
docker compose exec eclair php -r 'var_dump(function_exists("imagewebp"));'   # bool(true)
docker compose exec eclair apache2ctl -M | grep -E 'rewrite|headers|deflate'
```

## Checks after you deploy Éclair

```bash
SITE=https://exemple.fr
# 1. Internal files inaccessible: 404 expected for each one
for f in /data/site.php /data/users.php /app/bootstrap.php /cache/runtime.php /data/.htaccess; do
  printf '%s %s\n' "$(curl -s -o /dev/null -w '%{http_code}' "$SITE$f")" "$f"
done
# 2. URL rewriting: the controller answers "ok"
curl -s "$SITE/__eclair/ping"; echo
# 3. Static cache: X-Cache: HIT on the second call
curl -sI "$SITE/" > /dev/null; curl -sI "$SITE/" | grep -i x-cache
# 4. MCP connector turned on: 401 and WWW-Authenticate header
curl -si -X POST "$SITE/_eclair/mcp" | grep -i -E '^HTTP|www-authenticate'
```

Finish in the admin: the **Security** page must show a green protection test and a verified address, which requires the server to be able to call itself on its public address, even from a container. Finally, upload an image larger than 1 MB in **Media** and, if the AI assistant is turned on, check that its reply streams in as it's written.

## Key points

- Nginx ignores `.htaccess`: copy its rules while keeping `.well-known` reachable for the MCP connector.
- To deploy Éclair properly, allow 20 MB uploads and turn off FastCGI buffering for the AI assistant.
- Behind a proxy, send `X-Forwarded-Proto` and `Host`, and only choose an IP address header if it really exists.
- With Cloudflare: Full (strict) mode, `/_eclair/` and `/.well-known/` open, HTML kept out of the cache, Rocket Loader off.
- The Docker example keeps Apache and the original `.htaccess`, with `data/` and `media/` in volumes.

## Going further

- [Éclair's architecture](https://eclair.academy/en/paths/expert/architecture): why each `.htaccess` rule exists.
- [Performance: staying at 100 out of 100](https://eclair.academy/en/paths/expert/performance): measure the site once it's deployed.
- [Advanced security and maintenance](https://eclair.academy/en/paths/expert/advanced-security): file permissions, backups and updates.
- [Connect Claude to your site](https://eclair.academy/en/paths/ai-mcp/connect-claude): the MCP connector, which requires HTTPS.
- Nginx: [FastCGI module](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html), for `fastcgi_buffering` and `fastcgi_read_timeout`.
- Docker Hub: [official php image](https://hub.docker.com/_/php), its extensions and its `php.ini`.

## Frequently asked questions

### Should I keep the .htaccess files on Nginx?

They're useless on Nginx, but they do no harm and will be useful again if you go back to Apache. It's your Nginx configuration that protects the site.

### Why does the AI assistant reply all at once behind Nginx?

A buffer is holding back the stream: check `fastcgi_buffering off;` in the PHP block, or `proxy_buffering off;` if Nginx acts as a reverse proxy. The rest of the site isn't affected.

### Can Cloudflare cache my HTML pages?

It can, but Éclair will no longer be able to clear that cache on every publication, so your visitors would see old versions. Éclair's static cache already serves pages in a few milliseconds from your server.

### Do I need Docker to deploy Éclair?

No. Shared hosting with Apache or LiteSpeed is enough, and so is a VPS with Nginx. Docker is mainly useful to standardize deployments or to isolate several sites on the same machine.
