Excel & outils métier

Intranet local en HTML et JavaScript, sans serveur : comment ça marche

Créer un intranet local en HTML et JavaScript sans serveur : limites de file://, où stocker les données, API File System Access, sécurité et sauvegarde.

L’essentiel

  • Un intranet local est un petit site HTML, CSS et JavaScript posé dans un dossier partagé, que chacun ouvre dans son navigateur : aucun serveur, aucune installation, et tous les calculs se font sur le poste.
  • Ouvert depuis une adresse file://, il a trois limites : les modules JavaScript et fetch() ne fonctionnent pas, et le comportement de localStorage n’est pas défini. On livre donc des scripts classiques et on écrit les données de référence dans des fichiers .js.
  • Les données saisies peuvent vivre dans le navigateur (localStorage, IndexedDB), mais la copie de référence doit être un fichier exporté en JSON ou en CSV ; l’API File System Access permet de réécrire un fichier directement, dans Chrome et Edge mais pas dans Firefox ni Safari.
  • La seule protection d’un intranet local, ce sont les droits du dossier partagé : pas de secret dans le code, pas de données sensibles sans accès restreint.
  • Dès que plusieurs personnes doivent modifier les mêmes données en même temps, ou qu’il faut des comptes et un historique, il faut passer à un intranet avec serveur.
Sommaire10 parties
  1. Le principe : un dossier, une page, aucun serveur
  2. Les trois limites du protocole file://
  3. Où vivent les données : cinq options
  4. Export et import : la solution qui fonctionne partout
  5. Écrire directement dans un fichier : l’API File System Access
  6. Sécurité : ce que le dossier partagé protège, et ce qu’il ne protège pas
  7. Sauvegarde et mises à jour : les règles d’exploitation
  8. Quand passer à un intranet avec serveur
  9. En résumé : léger, fiable, à condition de respecter ses limites
  10. Questions fréquentes

Un intranet local est un petit site interne fait de fichiers HTML, CSS et JavaScript, posé dans un dossier partagé et ouvert directement dans le navigateur. Il n’a besoin ni de serveur, ni d’installation, ni de connexion internet : un double-clic sur index.html suffit, et tous les calculs se font sur le poste. Pour un outil de calcul, un simulateur ou un formulaire guidé, c’est souvent la solution la plus simple à déployer dans une PME.

C’est le format que j’ai utilisé chez mon employeur pour transformer des classeurs Excel complexes en mini-sites intranet locaux : mêmes formules, mais chaque feuille devient un écran clair et guidé. Encore faut-il connaître les règles du jeu, car le navigateur traite les fichiers locaux avec méfiance. Voici ce qui fonctionne, ce qui ne fonctionne pas, où ranger les données et à quel moment un serveur devient nécessaire.

Le principe : un dossier, une page, aucun serveur

L’application tient dans un seul dossier, organisé par exemple ainsi :

  • index.html : la page, avec ses écrans ;
  • css/app.css : la mise en forme ;
  • js/regles.js : les calculs, regroupés à un seul endroit et testables ;
  • js/app.js : l’interface, qui lit les saisies et affiche les résultats ;
  • donnees/baremes.js : les données de référence, comme les barèmes ou les listes de choix.

Quand on ouvre la page depuis le disque ou un partage réseau, l’adresse affichée commence par file:// au lieu de https:// (MDN). Le navigateur affiche la page, applique les styles et exécute normalement les scripts classiques : formulaires, calculs et impression fonctionnent, même hors ligne. En pratique, on dépose le dossier sur un partage accessible à l’équipe et on crée un raccourci vers index.html sur chaque poste.

Les trois limites du protocole file://

Pour des raisons de sécurité, de nombreux navigateurs, dont Firefox et Chrome, donnent désormais aux fichiers locaux une origine dite opaque : les chargements soumis aux règles CORS échouent entre fichiers locaux (MDN). Les scripts classiques, les feuilles de style et les images se chargent normalement ; le reste demande trois adaptations.

1. Pas de modules JavaScript

Un script chargé avec type="module" déclenche une erreur CORS quand la page est ouverte en file:// (MDN). Il faut donc des scripts classiques, chargés dans l’ordre, qui partagent un seul objet global pour ne pas se marcher dessus : window.App = window.App || {}; en tête de chaque fichier, puis App.calculerRemise = function (montant) { … };. Si vous développez avec des modules, un outil d’assemblage peut produire un fichier classique unique pour la livraison.

2. Pas de fetch() vers les fichiers voisins

Les requêtes fetch() et XMLHttpRequest soumises à CORS n’acceptent que les adresses HTTP et HTTPS : lire un baremes.json posé à côté de la page échoue (MDN). La parade est simple : écrire les données de référence dans un fichier JavaScript, App.BAREMES = [{ seuil: 0, taux: 0 }, { seuil: 1000, taux: 0.02 }];, chargé par une balise <script src="donnees/baremes.js"></script>. Pour lire un fichier fourni par l’utilisateur, on passe par un bouton d’import, décrit plus bas.

3. Un stockage du navigateur au comportement non garanti

Pour les pages ouvertes depuis une URL file:, le comportement de localStorage n’est pas défini et peut varier selon les navigateurs (MDN). Selon MDN, les navigateurs actuels semblent donner à chaque URL file: son propre espace, sans aucune garantie, et certains ont déjà changé de règle. Conséquence possible : la même page ouverte par un autre chemin (une lettre de lecteur réseau au lieu du chemin du serveur, par exemple) ou après un déplacement du dossier retrouve un espace vide. Préfixez chaque clé avec le nom de l’outil, comme 'devis:brouillon', entourez les accès d’un try…catch, et ne confiez jamais au navigateur la seule copie d’une donnée.

Où vivent les données : cinq options

Une application locale manipule deux sortes de données : les données de référence, que seul le responsable de l’outil modifie, et les données saisies par les utilisateurs. Chacune a sa place.

Les options de stockage d’une application HTML locale
OptionCe que c’estOù sont les donnéesUsage conseillé
Fichier .js de référenceBarèmes, listes et paramètres écrits en JavaScriptDans le dossier de l’applicationDonnées de référence, en lecture seule
localStoragePaires clé-valeur en texte, jusqu’à 5 Mio par origineDans le navigateur du postePréférences, brouillon en cours
IndexedDBBase d’objets asynchrone, avec index et transactionsDans le navigateur du posteHistorique local, volumes importants
Export et importL’utilisateur enregistre un fichier JSON ou CSV, puis le rouvreLà où l’utilisateur l’enregistreCopie de référence, échanges avec Excel
API File System AccessLecture et réécriture d’un fichier choisi par l’utilisateurSur le disque ou le partageSauvegarde directe, dans Chrome et Edge (pas Firefox ni Safari)

La limite de 5 Mio pour localStorage vient de la page de MDN sur les quotas de stockage. IndexedDB accepte des volumes bien plus importants, y compris des fichiers, mais il reste attaché à un navigateur, sur un poste. La règle à retenir : le navigateur sert de mémoire de travail, les fichiers servent de référence.

Pourquoi le navigateur n’est pas un lieu de stockage sûr

Les données d’un navigateur peuvent disparaître sans que l’utilisateur ait rien fait de mal :

  • par défaut, le stockage est dit « au mieux » : quand le disque manque de place, le navigateur peut effacer les données des sites les moins récemment utilisés, et il efface alors toutes les données d’une origine d’un coup (MDN) ;
  • quand sa protection contre le suivi intersites est activée, Safari supprime les données créées par script d’un site avec lequel l’utilisateur n’a eu aucune interaction pendant sept jours d’utilisation du navigateur (même source) ;
  • en navigation privée, localStorage est vidé à la fermeture du dernier onglet privé (MDN) ;
  • un nettoyage des données de navigation, un changement de navigateur ou de poste, et tout est perdu ;
  • ces données restent sur le poste : en général, elles ne profitent pas des sauvegardes du partage réseau.

On peut demander un stockage persistant avec navigator.storage.persist(), que le navigateur accorde ou non (MDN). C’est utile, mais pas suffisant : prévoyez toujours un export.

Export et import : la solution qui fonctionne partout

Le moyen le plus fiable de conserver et de partager des données sans serveur est aussi le plus ancien : un fichier. L’utilisateur clique sur « Exporter » et le navigateur télécharge un fichier ; il clique sur « Importer » pour le rouvrir.

  • Export : on transforme les données en texte avec JSON.stringify(donnees, null, 2), on en fait un Blob, puis un lien temporaire : a.href = URL.createObjectURL(blob); a.download = 'suivi.json'; a.click();. L’attribut download fonctionne avec les adresses blob: (MDN), donc aussi depuis une page locale.
  • Import : un champ <input type="file" accept=".json">, puis JSON.parse(await input.files[0].text()) (MDN). Vérifiez la structure et la version du fichier avant de remplacer quoi que ce soit.
  • CSV pour Excel : un CSV encodé en UTF-8 s’ouvre correctement par double-clic dans Excel s’il commence par une marque d’ordre d’octets, ou BOM (Microsoft) : new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8' }). Le séparateur de liste dépend des paramètres régionaux de Windows (Microsoft) : testez l’ouverture sur un poste de l’équipe.

Ajoutez dans chaque fichier exporté la date, la version de l’application et le nom de l’outil. Ces trois informations permettent, des mois plus tard, de savoir d’où vient un fichier et s’il peut encore être réimporté.

Écrire directement dans un fichier : l’API File System Access

Télécharger un nouveau fichier à chaque sauvegarde finit par encombrer le dossier Téléchargements. L’API File System Access va plus loin : l’utilisateur choisit un fichier une fois, et l’application peut le relire et le réécrire.

  • Support : showOpenFilePicker() et showSaveFilePicker() existent dans Chrome et Edge depuis la version 86 (et dans Opera), pas dans Firefox ni Safari, d’après les tables de compatibilité de MDN (MDN). L’API est toujours marquée comme expérimentale.
  • Conditions : elle exige un contexte sécurisé, ce qui inclut les adresses file: (MDN), et une action de l’utilisateur, comme un clic sur un bouton (MDN).
  • Écriture sûre : createWritable() écrit en général dans un fichier temporaire et ne remplace l’original qu’à la fermeture du flux (MDN). Une interruption en cours d’écriture ne laisse donc pas, en principe, un fichier à moitié écrit.

Le code tient en quelques lignes : const fichier = await window.showSaveFilePicker({ suggestedName: 'suivi.json' });, puis const flux = await fichier.createWritable(); await flux.write(texte); await flux.close();. Testez d’abord 'showSaveFilePicker' in window, et revenez à l’export classique si l’API manque.

Rien, dans ce fonctionnement, n’empêche deux postes d’enregistrer le même fichier : la dernière sauvegarde écrase la précédente. Organisez les fichiers pour éviter les conflits, avec un fichier par personne, par dossier traité ou par période, plutôt qu’un fichier commun.

Sécurité : ce que le dossier partagé protège, et ce qu’il ne protège pas

Une application locale n’a pas de comptes utilisateurs. Sa seule protection, ce sont les droits d’accès du dossier partagé, gérés par votre service informatique. Trois conséquences :

  • Tout ce qui est dans le dossier est lisible par ceux qui y ont accès, code compris. Un mot de passe vérifié en JavaScript n’est pas une protection : il suffit d’ouvrir le fichier pour le lire. Aucun secret, clé d’API ou identifiant, ne doit figurer dans le code.
  • Pas de données sensibles sans accès restreint : santé, salaires, évaluations. La CNIL recommande de limiter les accès aux seules données dont chaque utilisateur a besoin. Si le dossier est ouvert à toute l’entreprise, ces données n’ont rien à y faire.
  • L’intégrité compte autant que la confidentialité : donnez aux utilisateurs un accès en lecture seule au dossier de l’application, et l’écriture au seul responsable de l’outil. Sinon, n’importe qui peut modifier une formule dans regles.js, sans laisser de trace.

Pour les autres bonnes pratiques, voyez la checklist de sécurité d’une application web.

Sauvegarde et mises à jour : les règles d’exploitation

Un outil sans serveur reste un outil de production. Quelques règles suffisent à l’exploiter sereinement :

  • Les fichiers de données sont enregistrés sur un partage couvert par les sauvegardes : vérifiez-le avec le service informatique
  • Le code source est conservé dans un dépôt versionné, en dehors du dossier utilisé par l’équipe
  • Chaque version porte un numéro, affiché en pied de page et inscrit dans les fichiers exportés
  • Une mise à jour se teste dans un dossier de recette avant de remplacer le dossier partagé
  • Les calculs sont revérifiés à chaque version avec un jeu de cas de référence, comparé au classeur d’origine
  • L’application est testée sur les navigateurs réellement installés dans l’entreprise

Le jeu de cas de référence et la façon de comparer automatiquement les résultats au classeur sont décrits dans le guide recréer des formules Excel en JavaScript.

Quand passer à un intranet avec serveur

L’application locale atteint ses limites quand :

  • plusieurs personnes doivent modifier les mêmes données en même temps ;
  • il faut des comptes, des rôles et un historique de qui a fait quoi ;
  • l’outil doit lire les données d’un autre logiciel, comme un ERP, par une API ;
  • il doit être accessible hors du réseau de l’entreprise, ou sur mobile ;
  • les données sont sensibles et demandent des droits plus fins que ceux d’un dossier.

Il existe un palier intermédiaire : servir le même dossier par un serveur web interne. L’adresse passe en http://, et les modules JavaScript comme les appels fetch() vers les fichiers voisins fonctionnent sans autre changement. Pour essayer sur un poste, MDN propose par exemple la commande python3 -m http.server (MDN). Mais un serveur de fichiers n’ajoute ni comptes ni base de données. Pour cela, il faut une véritable application web, avec authentification, rôles et données isolées : c’est l’architecture que j’ai mise en place pour Docalio, mon portail client en ligne.

En résumé : léger, fiable, à condition de respecter ses limites

Un intranet local en HTML et JavaScript est la façon la plus légère de donner à une équipe un outil fiable : pas de serveur, pas d’installation, des calculs qui tournent hors ligne. Il demande trois précautions : des scripts classiques et des données de référence en fichiers .js, des fichiers exportés comme copie de référence, et un dossier dont les droits sont pensés. Dès que les données doivent être partagées en temps réel ou protégées par des comptes, passez à un serveur. Pour faire d’un classeur un outil de ce type, voyez la méthode dans transformer un fichier Excel en application web, ou mon accompagnement Excel en application web.

Questions fréquentes

Peut-on créer une application web qui fonctionne sans serveur ?

Oui. Un dossier contenant une page HTML, ses styles et ses scripts s’ouvre directement dans le navigateur et fait tous ses calculs sur le poste, même hors ligne. Il faut seulement respecter les limites des fichiers locaux : pas de modules JavaScript, pas de fetch() vers les fichiers voisins, et un stockage du navigateur qui ne sert que de mémoire de travail.

Pourquoi mon JavaScript ne fonctionne-t-il pas quand j’ouvre le fichier HTML en local ?

Le plus souvent, la page charge un module (type="module") ou lit un fichier avec fetch() : ouverts depuis une adresse file://, ces chargements déclenchent une erreur CORS. Remplacez les modules par des scripts classiques et écrivez les données dans un fichier .js, ou servez le dossier par un petit serveur local, par exemple avec python3 -m http.server.

localStorage fonctionne-t-il avec une page ouverte en file:// ?

En pratique oui, mais son comportement n’est pas défini pour les URL file: et peut changer d’un navigateur ou d’une version à l’autre. La même page ouverte par un autre chemin peut retrouver un espace vide, et le navigateur peut effacer les données. Préfixez les clés avec le nom de l’outil et gardez toujours une copie exportée dans un fichier.

Comment enregistrer un fichier depuis une page HTML locale ?

La méthode qui marche partout consiste à créer un Blob, puis un lien avec l’attribut download qui déclenche le téléchargement d’un fichier JSON ou CSV. Pour réécrire directement le même fichier, l’API File System Access et sa méthode showSaveFilePicker() fonctionnent dans Chrome et Edge, mais pas dans Firefox ni Safari.

Un intranet local en HTML est-il sécurisé ?

Il est aussi sûr que les droits du dossier partagé qui le contient, pas plus. Le code et les fichiers de données sont lisibles par toute personne qui a accès au dossier, et un mot de passe vérifié en JavaScript ne protège rien. Réservez-le à des données non sensibles, ou à un dossier dont l’accès est restreint aux bonnes personnes.

Sources

  1. Mettre en place un serveur de test localMDN Web Docs, 28 septembre 2025
  2. Raison : la requête CORS n’utilise pas HTTPMDN Web Docs
  3. Utiliser les modules JavaScriptMDN Web Docs
  4. Window.localStorageMDN Web Docs
  5. Storage quotas and eviction criteriaMDN Web Docs, 5 janvier 2026
  6. API IndexedDBMDN Web Docs
  7. StorageManager.persist()MDN Web Docs
  8. <a> : l’élément d’ancre (attribut download)MDN Web Docs
  9. Blob: text() methodMDN Web Docs
  10. Window: showOpenFilePicker() methodMDN Web Docs, 11 septembre 2026
  11. Window: showSaveFilePicker() methodMDN Web Docs, 11 septembre 2026
  12. FileSystemFileHandle: createWritable() methodMDN Web Docs, 4 juillet 2025
  13. Secure contextsMDN Web Docs
  14. Ouvrir correctement les fichiers CSV UTF-8 dans ExcelMicrosoft Support
  15. Importer ou exporter des fichiers texte (.txt ou .csv)Microsoft Support
  16. Sécurité : gérer les habilitationsCNIL, 13 mars 2024