Aller au contenu

Express : fichiers statiques, envois et vues

import express from 'express';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const dossier = path.dirname(fileURLToPath(import.meta.url));
const app = express();
app.use(
express.static(path.join(dossier, '..', 'public'), {
maxAge: '1d',
index: 'index.html',
dotfiles: 'ignore',
}),
);

public/style.css est alors disponible à /style.css. Préfixer l’URL :

app.use('/assets', express.static(path.join(dossier, '..', 'public')));

Utiliser des chemins absolus (construits avec import.meta.url) : un chemin relatif dépend du dossier courant au lancement.

En production, laisser le reverse proxy (Caddy, Nginx) servir les fichiers statiques : plus rapide et sans passer par Node.js (voir Sécurité et production).

app.get('/rapport.pdf', (req, res) => {
res.sendFile(path.join(dossier, '..', 'fichiers', 'rapport.pdf'));
});
app.get('/telecharger', (req, res) => {
res.download(
path.join(dossier, '..', 'fichiers', 'rapport.pdf'),
'rapport-2026.pdf',
);
});
Fenêtre de terminal
npm install multer
import multer from 'multer';
import crypto from 'node:crypto';
import path from 'node:path';
const televersement = multer({
storage: multer.diskStorage({
destination: '/srv/monapi/uploads',
filename: (req, fichier, cb) =>
cb(
null,
crypto.randomUUID() + path.extname(fichier.originalname).toLowerCase(),
),
}),
limits: { fileSize: 5 * 1024 * 1024, files: 1 },
fileFilter: (req, fichier, cb) => {
const autorises = ['image/png', 'image/jpeg', 'application/pdf'];
cb(null, autorises.includes(fichier.mimetype));
},
});
app.post('/api/fichiers', televersement.single('fichier'), (req, res) => {
if (!req.file) return res.status(400).json({ erreur: 'fichier refusé' });
res.status(201).json({ nom: req.file.filename, taille: req.file.size });
});
Fenêtre de terminal
curl -F 'fichier=@photo.jpg' http://127.0.0.1:3000/api/fichiers

Règles de sécurité pour les téléversements :

  • Générer le nom de stockage côté serveur (jamais le nom envoyé par le client).
  • Limiter la taille et le nombre de fichiers.
  • Contrôler le type (le mimetype envoyé par le client est falsifiable : pour des cas sensibles, contrôler le contenu).
  • Stocker hors du dossier servi en statique, ou dans un dossier où rien ne s’exécute.
  • Aligner la limite du reverse proxy (client_max_body_size pour Nginx).

Express sait produire des pages HTML à partir de modèles. Exemple avec EJS :

Fenêtre de terminal
npm install ejs
app.set('view engine', 'ejs');
app.set('views', path.join(dossier, '..', 'views'));
app.get('/clients', (req, res) => {
res.render('clients', {
titre: 'Clients',
clients: [{ nom: 'Alice' }, { nom: 'Bob' }],
});
});

views/clients.ejs :

<!doctype html>
<html lang="fr">
<head><meta charset="utf-8"><title><%= titre %></title></head>
<body>
<h1><%= titre %></h1>
<ul>
<% clients.forEach((c) => { %>
<li><%= c.nom %></li>
<% }) %>
</ul>
</body>
</html>

<%= … %> échappe le HTML (protection contre le XSS) ; <%- … %> insère sans échapper : à réserver à du HTML de confiance.

Fenêtre de terminal
npm install express-session
import session from 'express-session';
app.set('trust proxy', 1);
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 1000 * 60 * 60 * 8,
},
}),
);
app.post('/connexion', (req, res) => {
req.session.utilisateur = { id: 1 };
res.sendStatus(204);
});
Réglage Raison
httpOnly Cookie inaccessible au JavaScript
secure Envoyé en HTTPS seulement
sameSite: 'lax' Limite les requêtes intersites (CSRF)
secret Variable d’environnement longue et aléatoire (openssl rand -base64 48)

Le stockage de sessions par défaut est en mémoire : il est perdu à chaque redémarrage et inadapté au mode cluster ou à la production. Utiliser un stockage externe (par exemple Redis, voir Redis : installation).

Symptôme Piste
404 sur un fichier statique Chemin du dossier (utiliser un chemin absolu), nom de fichier, ordre des app.use
Error: Failed to lookup view views mal défini ou fichier absent
multer : LIMIT_FILE_SIZE Fichier trop gros : limite fileSize
413 Request Entity Too Large Limite du reverse proxy ou express.json({ limit })
Session perdue à chaque requête cookie.secure: true sans HTTPS ni trust proxy derrière le proxy
EACCES à l’écriture d’un envoi Droits du dossier de destination pour l’utilisateur du service

Ubuntu 24.04 et 26.04 : différences à connaître

Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »

Aucune. Veiller aux droits du dossier d’envois pour l’utilisateur qui exécute le service (voir systemd : écrire un service).