---
type: how-to
title: Recettes de proxy inverse et le contrat du callback
summary: Câble la porte de proxy sur Traefik ou Caddy, héberge le callback qui transforme un laissez-passer résolu en cookie, et protège Authelia sans y toucher.
---

Chaque proxy inverse a besoin des deux mêmes pièces : un contrôle **autorisateur** qui demande à Caputchin si une requête porte un laissez-passer valide, et un **callback** sur ton origine qui transforme un laissez-passer résolu en cookie `cpt_gate`. La [marche à suivre nginx](/docs/proxy-page-gate/set-up) montre la forme de bout en bout ; cette page donne les autres proxies, le contrat exact du callback, et un exemple Authelia complet. Remplace `YOUR_SITE_KEY` par ta clé publique partout.

Les endpoints sont les mêmes partout :

- **Autorisateur** : `GET https://verify.caputchin.com/v1/gate/authz?site=YOUR_SITE_KEY`, avec l'en-tête `Cookie` du visiteur transmis. `204` veut dire autoriser, `401` veut dire défi.
- **Défi** : redirige le navigateur vers `https://verify.caputchin.com/v1/gate/challenge?site=YOUR_SITE_KEY&return=<l'URL originale>`.

## Traefik

Utilise un middleware `forwardAuth` pour l'autorisateur. Traefik transmet l'en-tête `Cookie` entrant par défaut. Sur un `401` du middleware, redirige le navigateur vers le défi (un middleware de page d'erreur pointé vers une petite route qui émet le `302`, ou gère le `401` à ton edge).

```yaml
http:
  middlewares:
    cpt-gate:
      forwardAuth:
        address: "https://verify.caputchin.com/v1/gate/authz?site=YOUR_SITE_KEY"
        # The 401 from forwardAuth is your signal to send the browser to:
        #   https://verify.caputchin.com/v1/gate/challenge?site=YOUR_SITE_KEY&return=<original-url>
  routers:
    portal:
      rule: "Host(`auth.example.com`)"
      middlewares: ["cpt-gate"]
      service: your-app
```

## Caddy

Utilise `forward_auth` pour l'autorisateur et `handle_errors` pour transformer le `401` en redirection. C'est un point de départ ; ajuste la syntaxe des directives pour ta version de Caddy.

```caddy
auth.example.com {
  forward_auth https://verify.caputchin.com {
    uri /v1/gate/authz?site=YOUR_SITE_KEY
    copy_headers Cookie
  }
  handle_errors {
    @challenge expression {http.error.status_code} == 401
    redir @challenge https://verify.caputchin.com/v1/gate/challenge?site=YOUR_SITE_KEY&return={scheme}://{host}{uri} 302
  }
  reverse_proxy your-app:8080
}
```

## Le callback

La page de défi hébergée se termine en auto-envoyant un formulaire `POST` vers `https://<ton-origine>/__cpt/callback` avec deux champs dans le **corps** : `cpt_gate` (le laissez-passer) et `to` (où envoyer le visiteur). Ajoute cette seule route à ton origine (ou comme une location dédiée sur le proxy). Elle doit :

1. Lire `cpt_gate` et `to` depuis le **corps** du POST, jamais l'URL (un laissez-passer dans une URL fuit dans les logs et les référents).
2. Confirmer que `to` est sur ta propre origine, et rejeter tout le reste, pour que le callback ne puisse pas être transformé en open redirect.
3. Poser le cookie avec les attributs ci-dessous.
4. Rediriger (`303`) vers `to`.

```js
// Any tiny handler on your origin works. Express shown; ~15 lines.
app.post("/__cpt/callback", express.urlencoded({ extended: false }), (req, res) => {
  const pass = String(req.body.cpt_gate || "");
  const to = String(req.body.to || "/");
  const target = new URL(to, `https://${req.headers.host}`);
  if (target.host !== req.headers.host) return res.status(400).end(); // same-origin only
  res.setHeader(
    "Set-Cookie",
    `cpt_gate=${pass}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=1800`
  );
  res.redirect(303, target.pathname + target.search);
});
```

Les attributs du cookie sont tout le modèle de sécurité. Comme le laissez-passer n'est pas lié à un appareil, `HttpOnly` (bloque le vol par script), `Secure` (HTTPS seulement), `SameSite=Lax`, et un `Max-Age` court (à faire correspondre à ton TTL de laissez-passer) sont ce qui le protège. Aussi : ne journalise pas la valeur `cpt_gate` dans tes access logs, et garde `Referrer-Policy: no-referrer` sur le callback pour que le laissez-passer ne voyage jamais dans un en-tête référent.

## Exemple travaillé : Authelia

Le portail de connexion d'Authelia est une app mono-page compilée sans place pour incruster le widget et sans hook de plugin, donc la porte est le moyen de le protéger. Authelia est déjà derrière un proxy inverse (c'est ainsi que son propre ForwardAuth fonctionne), et ce proxy est là où va la porte. Authelia lui-même reste complètement inchangé.

Mets la porte sur le vhost du portail (par exemple `auth.example.com`) :

- **Mets une porte** au HTML du portail et à l'endpoint de connexion de premier facteur (`/api/firstfactor`).
- **Exclus** l'endpoint verify du propre ForwardAuth d'Authelia (`/api/verify`), que le proxy appelle pour chaque requête upstream protégée. Y mettre une porte casserait chaque app protégée derrière Authelia.
- **Exclus** les health checks et les assets statiques dont la page du défi elle-même a besoin.
- **Ajoute** la route `/__cpt/callback` comme ci-dessus.

La porte tient les bots hors du portail ; la Regulation intégrée d'Authelia (limite de réessais et bannissement) plafonne toujours les réessais d'identifiants pour quiconque passe, donc les deux couches se cumulent. La porte protège seulement le portail interactif (l'UI de connexion et de consentement) ; elle ne met pas de porte, et ne devrait pas, aux endpoints de token non interactifs.

## Voir aussi

- [Configurer la porte de proxy](/docs/proxy-page-gate/set-up) : la marche à suivre nginx et le déploiement mode-aperçu-d'abord.
- [Vue d'ensemble](/docs/proxy-page-gate/overview) : le concept et le cookie de laissez-passer.
- [Statistiques](/docs/proxy-page-gate/statistics) : confirme le câblage en observant défi contre passage direct.
