4 Expert pathLesson 4 of 8 6 min read

Nginx, VPS, proxy and CDN: deploy Éclair

  1. 1. Éclair's architecture
  2. 2. Local development and testing
  3. 3. Customize the theme templates
  4. 4. Nginx, VPS, proxy and CDN: deploy Éclair
  5. 5. Éclair performance: staying at 100 out of 100
  6. 6. Migrate from WordPress or Wix
  7. 7. Advanced security and maintenance
  8. 8. Scaling Éclair in an agency
Contents
  1. What Nginx must reproduce from .htaccess
  2. The commented Nginx configuration
  3. HTTPS with Certbot, then Éclair's settings
  4. How do you deploy Éclair behind a reverse proxy?
  5. Cloudflare: what to configure
  6. How do you deploy Éclair with Docker?
  7. Checks after you deploy Éclair
  8. Key points
  9. Going further
  10. Frequently asked questions

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.

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 ruleWhy
app/, data/ and cache/ return 404code, 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.phpa script dropped elsewhere never runs
media/ without scripts or pagesa 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 cachinglight 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.

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:

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

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 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;
}

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.

SituationChoice in Éclair
no proxy, or address already corrected by Nginx's realip moduleNo (recommended)
Nginx as a reverse proxy with proxy_set_header X-Real-IPX-Real-IP
a proxy that appends to X-Forwarded-ForX-Forwarded-For
Cloudflare in front of the site, with or without a proxy behind itCloudflare

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.

Cloudflare: what to configure

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.

# 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/
# 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).

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

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

Going further

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.

All the lessons of this path