Capture d'écran du tableau de bord de Calendly

Remplacer Calendly par une solution auto-hébergée : comparatif et audit de code

29 juillet 2026

cal.com, calendly, zcal… Pour la prise de rendez-vous en ligne, les solutions SaaS ne manquent pas, et sont globalement intéressantes. Une offre gratuite avec les fonctionnalités de base suffit même à bon nombre d’indépendants. À condition d’accepter de leur servir de vitrine (rien n’est vraiment gratuit…). Et d’accepter d’avoir vos données hébergées ailleurs, quelque part – on ne sait jamais vraiment où, et on ne sait jamais tout.

Un dysfonctionnement rencontré sur un service SaaS a été le déclencheur : la confiance accordée par défaut à un outil tiers, simplement parce qu’il est largement utilisé et facturé chaque mois, ne se justifie pas toujours. Cet épisode a suffi pour reconsidérer l’ensemble des services externes auxquels ce site déléguait des fonctions pourtant simples à internaliser – et la prise de rendez-vous en ligne, confiée jusque-là à Calendly, en faisait partie.

Calendly, qui, en soi, n’avait rien d’un mauvais outil. Je n’avais rien à lui reprocher.

Mais au-delà de l’épisode qui a servi de déclencheur, plusieurs contraintes structurelles s’accumulaient depuis un moment.

Pour une fonctionnalité somme toute simple – afficher des créneaux disponibles et enregistrer un choix :

  • je faisais appel à un service externe, qui lui en échange faisait sa pub sur mon site.
  • j’avais une dépendance à un service tiers pour une brique qui, techniquement, n’a rien d’exceptionnel.

Et la personnalisation reste bridée par ce que l’éditeur propose, sans possibilité d’aller au-delà.

Tous ces points ont alimenté ma réflexion, mais la contrainte majeure – une contrainte d’intégration – était ailleurs : ce site repose sur Astro, avec des pages statiques légères, un choix technique pensé justement pour éviter d’alourdir le site avec des scripts et dépendances externes – l’inverse de ce qu’implique l’ajout d’un widget tiers chargé depuis un autre domaine.

Restait à savoir si une alternative auto-hébergée pouvait tenir la comparaison : offrir le même service, avec le même niveau de finition pour le visiteur, sans dépendre d’un éditeur externe pour une fonctionnalité aussi centrale que la prise de contact. C’est cette recherche que documente la suite de cet article.

Le paysage des alternatives

Ce que je cherchais concrètement :

  • une prise de rendez-vous en ligne pour un visiteur anonyme ;
  • un hébergement sur mon infrastructure, données comprises ;
  • des confirmations et rappels automatiques ;
  • une intégration légère dans un site statique ;
  • la possibilité de personnaliser le parcours si un besoin métier spécifique émerge.

La recherche d’un remplaçant s’est vite heurtée à un constat : l’offre de solutions de prise de rendez-vous auto-hébergées, à jour et réellement pensées pour un visiteur anonyme qui réserve en ligne, est plus restreinte qu’on ne l’imagine. Beaucoup de projets trouvés en cours de route s’avéraient soit être de simples habillages visuels dépendant encore d’un service tiers en coulisses, soit des outils conçus avant tout pour la gestion interne de plannings, avec l’expérience du visiteur reléguée au second plan.

Quatre candidats m’ont paru sortir du lot :

  • CloudMeet, un projet léger et récent, construit sur une infrastructure d’hébergement à la demande, capable de tourner gratuitement pour un usage individuel. Sa qualité principale : une expérience de réservation soignée, visuellement proche de ce qu’on attend d’un service professionnel. Sa limite : un projet jeune, porté par une seule personne, avec un historique encore trop court pour juger de sa fiabilité dans la durée.
  • Le fork communautaire de cal.com, apparu après que l’éditeur historique a fermé le code source de sa solution principale au printemps 2026, invoquant des risques de sécurité liés à l’exposition de son code à des outils d’analyse automatisée. Ce fork conserve l’essentiel du moteur de réservation dans une licence libre, porté par d’anciens membres de l’équipe d’origine. Le plus complet des quatre sur le papier, mais le plus jeune en tant que projet communautaire indépendant – sa propre documentation déconseille encore un usage en production sans réserve.
  • booking-calendar, un projet minimaliste pensé pour un gestionnaire unique plutôt qu’une équipe, avec une synchronisation bidirectionnelle vers n’importe quel calendrier standard plutôt qu’une dépendance à un fournisseur particulier. Sa légèreté technique en fait un candidat sérieux pour un usage individuel, mais son interface de réservation, plus proche d’un outil de gestion que d’une vitrine commerciale, manque du soin visuel des solutions plus établies.
  • Easy!Appointments, une solution mature en développement actif depuis une dizaine d’années, avec une communauté large et une offre commerciale parallèle qui témoigne d’un usage réel en entreprise. C’est historiquement le candidat le plus éprouvé des quatre, et celui dont l’interface de réservation se rapproche le plus de ce qu’offrent les services professionnels payants.

Le choix final s’est joué sur un croisement de critères pratiques :

  • la facilité d’intégration dans un site existant sans dépendance à un langage serveur particulier,
  • la qualité de l’expérience proposée au visiteur,
  • la gestion native des obligations de conformité (recueil de consentement),
  • et un niveau de maturité suffisant pour un usage en clientèle réelle plutôt qu’un projet encore expérimental.

Le critère décisif : l’expérience de réservation

J’ai rapidement écarté deux des quatre candidats, pour la même raison au fond : un manque de recul suffisant pour un usage en production réelle. CloudMeet, porté par un seul développeur, ne comptait ni assez d’historique ni assez de retours d’utilisateurs pour juger sereinement de sa fiabilité. Le fork communautaire de cal.com, plus complet sur le papier, se trouvait dans une situation comparable – trop jeune en tant que projet indépendant, sa propre documentation déconseillant encore un usage en production sans réserve. L’avenir nous dira si l’un ou l’autre deviendra un challenger sérieux ou restera un one-shot project.

Restaient deux candidats à comparer plus finement : booking-calendar et Easy!Appointments.

Sur le papier, le candidat le plus léger et le plus proche de l’architecture technique déjà en place semblait le mieux placé. Mais un point l’a fait reculer dans le classement final : sa page de réservation, destinée au visiteur, se présentait comme une simple liste défilante de créneaux horaires – fonctionnelle, mais visuellement à mille lieues de ce à quoi les services professionnels payants ont habitué le public. Pas de calendrier mensuel à parcourir, pas de mise en scène du choix de date, juste une succession de lignes de texte à faire défiler.

Ce constat a soulevé une question : est-il possible de combiner les forces de plusieurs projets – la légèreté et l’intégration native au calendrier de l’un, l’interface soignée d’un autre ? En théorie séduisante, l’idée s’est révélée peu praticable dans les faits : les projets reposent sur des architectures techniques trop différentes pour être assemblés sans réécrire l’un des deux presque intégralement. Fusionner deux outils bâtis sur des bases incompatibles revient souvent à créer plus de travail que d’en choisir un seul et l’adapter.

La solution retenue a donc été de séparer les usages plutôt que les fusionner : un outil dédié à la prise de rendez-vous visible du public, choisi avant tout pour la qualité de son expérience de réservation, et – si un besoin de synchronisation plus poussée avec un calendrier auto-hébergé se présente un jour – un second outil pour cet usage interne, sans chercher à les faire cohabiter dans un même système.

Ce choix a fini par trancher en faveur de la solution la plus établie des quatre candidats : son interface de réservation, avec un calendrier mensuel classique et un parcours en plusieurs étapes, tient la comparaison avec les services professionnels qu’elle est censée remplacer – un point qui compte autant que la robustesse technique quand l’outil sert de vitrine à des prospects.

L’audit de code : une même faille dans deux projets, à dix ans d’écart de maturité

Avant de retenir une solution, j’ai voulu vérifier un point précis : que se passe-t-il si deux visiteurs cliquent sur le même créneau au même moment ? Ce n’est pas une question théorique – c’est le genre de scénario qui ne se manifeste jamais en développement solo et qui explose le jour où un lien de réservation circule un peu (une newsletter, un post LinkedIn, l’ouverture d’un nouveau lot de créneaux à heure fixe).

Le problème s’appelle une race condition de type TOCTOU (time-of-check to time-of-use) : le code vérifie qu’un créneau est libre, puis l’écrit en base – et entre les deux, rien n’empêche une autre requête concurrente de faire exactement la même chose sur le même créneau. Si la vérification et l’écriture ne sont pas protégées ensemble par un verrou, les deux requêtes peuvent chacune conclure « c’est libre » avant que l’une des deux ne valide son insertion.

Premier candidat : booking-calendar. Ce projet léger (React/TypeScript/Bun/SQLite, architecture propre séparant accès aux données et logique métier) vérifie bien l’absence de chevauchement avant d’enregistrer un rendez-vous, mais sans verrou explicite ni contrainte d’exclusion en base. Sous SQLite, ça ne pose jamais de problème visible : le moteur sérialise nativement les écritures, un seul rédacteur à la fois. C’est un filet de sécurité gratuit, propre au moteur – pas une protection posée consciemment par le code. Le jour où ce projet migre vers un moteur de base de données plus classique (une évolution naturelle envisagée par son architecture, justement conçue pour être portable), ce filet disparaît : deux réservations concurrentes peuvent chacune constater « aucun chevauchement » avant que l’une des deux ne soit réellement enregistrée.

Deuxième candidat : Easy!Appointments. Dix ans de développement, plus de 3000 étoiles sur GitHub, une offre payante depuis des années. L’hypothèse de départ semblait raisonnable : plus de trafic réel en production, donc plus de chances qu’un bug de ce type ait déjà été rencontré et corrigé. L’hypothèse ne résiste pas à la lecture du code.

Le flux public de réservation vérifie bien la disponibilité du créneau avant l’insertion – mais plusieurs opérations métier s’intercalent entre les deux (création du client, enregistrement des consentements RGPD, génération d’un lien de visioconférence), élargissant encore la fenêtre de risque. Et l’enregistrement final en base ne comporte ni transaction, ni verrou, ni contrainte d’unicité. Un commentaire dans le code source affirme pourtant qu’il n’est pas possible que deux clients réservent le même créneau simultanément – une intention documentée, mais qui n’est pas ce que le code fait réellement.

La trouvaille la plus intéressante : le projet contient bien, ailleurs dans son code, une fonction qui fait le bon calcul de chevauchement, avec une requête correctement construite. Cette fonction n’est simplement jamais appelée dans le flux de réservation publique. La brique existe, elle est correcte, elle est absente à l’endroit où elle compterait le plus.

Aucun des deux projets n’est « plus robuste » que l’autre sur ce point précis – les deux partagent le même défaut de conception, indépendamment de leur maturité respective. La réputation, l’ancienneté, une offre commerciale établie : ce sont des indices statistiques raisonnables, pas des preuves. La seule façon de savoir si un projet open source protège correctement contre ce genre de scénario, c’est de lire le code qui fait réellement le travail – pas celui qui prétend le faire dans un commentaire.

Le choix final ne s’est donc pas joué sur ce critère (les deux présentent la même faiblesse), mais sur d’autres facteurs : légèreté de la stack, intégration native avec les calendriers standards, qualité de l’expérience de réservation pour le visiteur, et gestion des consentements RGPD – présente chez Easy!Appointments via un mécanisme natif à activer, absente chez booking-calendar.

La mise en production : une intégration simple, un blocage inattendu

Techniquement, l’intégration d’Easy!Appointments dans le site n’avait rien de complexe : le service tourne sur son propre sous-domaine, indépendant du reste du site, et la page de contact se contente d’afficher sa page de réservation dans un cadre intégré, sans faire tourner la moindre ligne de code PHP sur le site public. C’est le principe même de la séparation que je cherchais au départ : un service self-hosted à part, un site vitrine qui reste léger et n’exécute que ce qui lui est propre.

Premier test après déploiement : le cadre refusait de s’afficher, avec un message d’erreur explicite dans la console du navigateur – le service refusait explicitement d’être affiché dans un cadre provenant d’un autre domaine.

Une fausse piste raisonnable. La première hypothèse, la plus logique compte tenu de l’architecture du serveur (un panneau d’administration avec une couche de proxy devant les sites hébergés), était que ce blocage provenait de cette couche intermédiaire plutôt que du service lui-même. Une tentative de correction a été appliquée directement à ce niveau, via la configuration standard qui permet d’ajouter ou de retirer des en-têtes de réponse. Sans effet.

Le vrai diagnostic. La cause réelle se trouvait dans le code source d’Easy!Appointments lui-même : une protection de sécurité, activée par défaut, qui interdit sciemment l’affichage de toutes les pages du service – y compris la page de réservation publique – dans un cadre provenant d’un autre site. Une mesure pensée pour protéger l’interface d’administration contre une technique d’attaque connue (le détournement de clic, où une page malveillante superpose un cadre invisible pour piéger un utilisateur), mais appliquée sans distinction à l’ensemble du service, y compris la partie destinée précisément à être partagée et intégrée ailleurs.

La correction du côté de la couche serveur, tentée en premier, ne pouvait techniquement pas suffire : le service applique cette protection directement dans son propre code, à un moment du traitement de la requête qui intervient après la tentative de correction côté serveur. Le correctif efficace a donc consisté à modifier ce comportement à la source, en autorisant explicitement le domaine du site à afficher la page de réservation en cadre, tout en conservant la protection pour l’interface d’administration.

Ce n’est pas un cas isolé : beaucoup d’outils auto-hébergés appliquent par défaut des protections de sécurité pensées pour un usage autonome, sans anticiper qu’une partie de leurs pages puisse être délibérément partagée ou intégrée ailleurs. Ce n’est pas un défaut de conception – la prudence par défaut est la bonne posture pour un logiciel qu’on installe soi-même sans savoir à l’avance comment il sera utilisé. Mais ça signifie qu’une intégration en apparence anodine (afficher une page dans un cadre) peut nécessiter d’aller lire le code plutôt que de se contenter de la documentation, quand le comportement observé ne correspond pas à ce qu’on attendait.

Bilan

Le résultat concret : un abonnement mensuel en moins, une donnée client (nom, email, motif de rendez-vous) qui reste sur une infrastructure propre plutôt que chez un tiers, et un site qui garde son poids de page initial intact, sans script externe ajouté pour cette seule fonctionnalité.

Ce n’est pas un choix qui s’imposait par défaut, ni le plus rapide des quatre à mettre en œuvre. Il a demandé une comparaison sérieuse entre plusieurs projets, une vérification du code plutôt qu’une confiance aveugle dans la réputation d’un outil établi, et un peu de temps de mise au point une fois en production. Mais c’est précisément ce travail de vérification qui distingue une migration hors SaaS bien menée d’un simple changement d’outil : s’assurer que la solution de repli tient réellement ses promesses, avant de la confier à des clients réels.

C’est ce type d’accompagnement – audit d’une solution existante ou candidate, migration hors SaaS, intégration technique sur-mesure – que je propose aux TPE et PME qui souhaitent reprendre la main sur leurs outils numériques plutôt que d’accumuler les abonnements. En discuter →

Partager :