Sécurité WordPress : configurer correctement CORS et protections associées

Quand on parle de sécurité WordPress, beaucoup de discussions finissent par tourner autour des plugins, des mises à jour, des mots de passe et du durcissement du serveur. C’est normal, mais il existe une classe de problèmes plus sournoise, parce qu’elle touche à la façon dont votre site autorise ou refuse l’accès aux ressources depuis d’autres origines. CORS en fait partie.

CORS (Cross-Origin Resource Sharing) n’est pas, à lui seul, une barrière de sécurité. C’est un mécanisme de contrôle côté navigateur, qui aide à limiter qui peut consommer vos endpoints depuis un domaine différent. Or, sur WordPress, on a souvent des APIs exposées, que ce soit via REST API, via des plugins, via WooCommerce, via des endpoints d’authentification, ou encore via des requêtes internes déclenchées par du JavaScript front.

Configurer CORS correctement devient alors un travail d’équilibriste. Trop restrictif, votre application front ne chargera plus les données, et vous passerez des heures à chercher pourquoi le navigateur bloque tout. Trop permissif, vous augmentez la surface d’exposition, et vous compliquez le travail de prévention contre les usages non désirés.

Je vous propose une approche pragmatique, centrée sur WordPress, en expliquant comment raisonner sur CORS, comment le relier à d’autres protections utiles, et surtout comment éviter les pièges classiques.

CORS, ce que le navigateur fait vraiment (et ce qu’il ne fait pas)

CORS intervient quand un navigateur fait une requête HTTP depuis une page servie par une origine vers une autre origine. L’origine se définit généralement par le triplet protocole, domaine, port. Le navigateur compare cette origine avec la politique renvoyée par le serveur à travers des en-têtes.

Concrètement, votre serveur peut répondre avec des en-têtes comme :

  • Access-Control-Allow-Origin : indique quelles origines sont autorisées
  • Access-Control-Allow-Methods : méthodes HTTP autorisées
  • Access-Control-Allow-Headers : en-têtes autorisés
  • Access-Control-Allow-Credentials : autorisation d’envoyer des cookies et identifiants
  • Vary: Origin : utile quand la réponse dépend de l’origine

Mais retenez un point important : CORS ne protège pas contre un client qui n’est pas un navigateur. Un script côté serveur, un outil comme curl, ou n’importe quel client HTTP peut appeler votre endpoint sans “bénéficier” des garde-fous CORS. La protection réelle doit donc exister aussi côté serveur, via l’authentification, la validation des entrées, le contrôle d’accès, et les règles d’exposition.

En pratique, CORS sert à empêcher le navigateur d’exposer vos réponses à un code JavaScript chargé d’une autre origine. Ce mécanisme peut réduire le risque, mais il ne remplace pas une vraie logique d’autorisation.

Où WordPress a tendance à exposer des points d’entrée

WordPress expose plusieurs routes et comportements qui peuvent devenir sensibles selon votre configuration et vos plugins.

Le premier grand classique, c’est la REST API. Les URLs ressemblent souvent à quelque chose comme /wp-json/.... Selon votre thème, vos plugins et vos besoins, vous pouvez y avoir des endpoints publics, des endpoints nécessitant authentification, ou des endpoints qui déclenchent des actions (création, modification, recherche).

Ensuite, il y a le reste des réponses HTTP : fichiers statiques, requêtes AJAX, endpoints fournis par des extensions, webhooks, ou encore des flux déclenchés par un front déporté (SPA, application mobile webview, ou simple découplage front/back).

Le piège récurrent que j’ai vu en mission, c’est la demande “Il faut que ça marche depuis mon domaine front”. Souvent, la solution choisie est trop large, par exemple autoriser toutes les origines. Ça “marche” dans le navigateur, mais ça rend votre serveur moins difficile à sonder, et ça augmente le risque de confusion entre authentification, cookies, et endpoints sensibles.

Définir une politique CORS utile : commencer par l’exigence réelle

La meilleure façon de ne pas se tromper, c’est de partir du besoin réel plutôt que de copier une configuration générique.

Posez-vous ces questions, sans jargon :

  • Votre front est-il sur le même domaine que WordPress, ou sur un autre domaine ?
  • Votre front doit-il envoyer des cookies de session, des headers d’authentification, ou bien juste lire des données publiques ?
  • Avez-vous besoin de méthodes comme POST, PUT, DELETE, ou uniquement GET ?
  • Quelles routes doivent être concernées par CORS, et lesquelles doivent rester fermées ?

Le résultat de ces réponses détermine le niveau d’ouverture.

Si votre SPA consomme uniquement des données publiques et ne nécessite pas de cookies, vous pouvez rester très strict : autoriser uniquement l’origine de votre front, limiter les méthodes, et éviter les credentials.

Si, au contraire, vous avez besoin d’envoyer des cookies (par exemple si vous comptez sur la session WordPress pour protéger l’API), la configuration devient plus délicate. Dès que vous activez les credentials, vous devez éviter toute généralisation du type “toutes origines”. Sinon, vous vous exposez à des comportements indésirables et vous cassez la logique même de contrôle.

Le cas le plus fréquent : autoriser une origine précise, limiter tout le reste

Un schéma sain, dans la majorité des déploiements, ressemble à ceci : vous autorisez uniquement votre origine front (par exemple https://app.exemple.com) et vous n’ouvrez pas plus que nécessaire.

Sur un serveur HTTP, on règle les en-têtes CORS au niveau des réponses. Sur WordPress, on peut aussi le faire via un plugin ou via du code, selon l’environnement. L’important est moins l’outil que le résultat final : les en-têtes doivent être cohérents, et ils doivent s’appliquer à la bonne partie de l’URL.

Exemple mental (sans imposer une syntaxe spécifique) :

  • Pour les requêtes OPTIONS (prévol), renvoyer des en-têtes expliquant les méthodes et headers autorisés.
  • Pour les requêtes réelles, renvoyer Access-Control-Allow-Origin avec une valeur qui correspond à l’origine attendue.
  • Quand vous autorisez des cookies, renvoyer Access-Control-Allow-Credentials: true, et ne jamais utiliser une réponse “wildcard” pour l’origine.

Un détail qui compte : quand votre serveur décide de renvoyer une valeur d’origine dynamique selon la requête, il est utile d’ajouter Vary: Origin afin d’éviter que des caches se trompent. En environnement WordPress avec reverse proxy et cache, ce point a déjà causé des surprises, notamment lors de bascules entre environnements de test et production.

Le piège des credentials : attention à la combinaison origine large et cookies

Les cookies, les sessions, et certains mécanismes d’authentification rendent la configuration CORS plus sensible.

Si votre front doit envoyer des cookies, vous devez avoir une politique claire sur :

  • Quelles origines sont autorisées à faire des requêtes cross-origin
  • Quelles routes acceptent effectivement des cookies
  • Et comment votre WordPress vérifie l’autorisation côté serveur

CORS ne “fait” pas l’autorisation à votre place. Il bloque seulement la lecture de la réponse par JavaScript, côté navigateur, si la politique CORS ne correspond pas. Mais si votre endpoint côté serveur accepte des cookies et ne contrôle pas correctement l’autorisation, alors un client non navigateur ou un scénario mal cadré peut exploiter cette faiblesse.

Je l’ai vécu avec un endpoint “d’apparence anodine”, utilisé pour remplir une partie d’un formulaire. Les cookies étaient reconnus, l’endpoint répondait avec des détails, et côté navigateur tout semblait bloqué jusqu’au moment où une configuration CORS trop large a été ajoutée pour “tester”. Le jour où un tiers a pu appeler depuis un autre contexte, on a découvert que l’endpoint divulguait plus que ce qui était prévu. Rien d’exceptionnel dans WordPress lui-même, mais une somme de petites décisions.

Le bon réflexe consiste à traiter CORS comme une barrière supplémentaire, jamais comme la seule protection. L’endpoint doit rester robuste même si CORS ne s’applique pas.

Prévol OPTIONS : ce que vous devez tester, surtout avec WordPress

Les navigateurs envoient très souvent une requête de prévol en OPTIONS quand la requête réelle est “complexe” (headers non simples, méthodes non simples, ou certaines valeurs). Si votre configuration CORS ne couvre pas correctement OPTIONS, vous verrez des erreurs de type “blocked by CORS policy”, parfois sans information claire côté serveur.

Sur WordPress, cela se manifeste souvent comme suit :

  • Votre front échoue uniquement sur certains endpoints
  • Le problème apparaît après un changement de header, par exemple l’ajout d’un header Authorization ou X-Requested-With
  • Les requêtes GET marchent, mais POST échoue

Ce que je recommande est simple : tester explicitement les prévols depuis votre environnement de front, ou utiliser des outils de diagnostic pour confirmer que la réponse OPTIONS inclut bien les en-têtes nécessaires, et que le serveur renvoie un statut cohérent.

Il ne faut pas non plus surcorriger. Il vaut mieux renvoyer exactement ce qui est requis, plutôt que de répondre “open bar” à n’importe quelle origine et n’importe quelle méthode.

Mettre CORS en relation avec la surface WordPress (auth, rôles, nonces)

Une configuration CORS “propre” ne devrait jamais donner accès à des actions non prévues. Dans WordPress, l’accès à certaines actions repose sur des mécanismes côté application : authentification, rôles, permissions, nonce, vérification de capacités.

Selon votre cas, il existe un point à ne pas perdre de vue : si vous développez un front découplé, il est tentant de s’appuyer uniquement sur CORS pour “autoriser” un flux. En réalité, le contrôle d’accès doit rester côté WordPress.

Deux exemples concrets :

  • Si une action nécessite que l’utilisateur soit connecté, l’endpoint doit refuser les appels non authentifiés, même si CORS “aurait permis” la lecture côté navigateur.
  • Si une action est sensible, vous voulez aussi une preuve d’intention (nonce ou token approprié), et pas juste une origine autorisée.

Sur un site WordPress mature, on voit souvent des plugins qui mettent en place des validations, mais pas toujours sur les endpoints custom. Quand CORS arrive après coup, il peut accidentellement rendre un endpoint custom accessible depuis plus d’endroits, ce qui fait remonter des bugs de permission.

Une stratégie robuste : réduire, segmenter, vérifier

Au lieu de chercher “la bonne recette CORS”, cherchez une méthode qui limite les mauvaises surprises.

Je fonctionne en trois temps quand j’aie à corriger un cas réel :

D’abord, je recense les endpoints réellement consommés cross-origin. L’objectif est de ne pas appliquer CORS partout. Sur WordPress, beaucoup de réponses ne doivent jamais être cross-origin, surtout si elles contiennent des informations sensibles.

Ensuite, je fais une matrice de comportements : pour chaque endpoint, on détermine si l’authentification est requise, si des cookies doivent être envoyés, et quelles méthodes sont utilisées par le front.

Enfin, je vérifie avec un test de bout en bout depuis un navigateur réel (pas un outil qui ignore la politique), et je contrôle côté serveur les statuts et les en-têtes, y compris sur les OPTIONS.

Voici une liste courte des points que je contrôle systématiquement, car ce sont ceux qui cassent le plus souvent en production :

  • Vérifier que seules les origines attendues sont autorisées, pas “toutes”
  • S’assurer que les requêtes OPTIONS renvoient les bons en-têtes CORS
  • Limiter les méthodes et headers à ce qui est réellement utilisé par le front
  • Quand des cookies sont nécessaires, activer Access-Control-Allow-Credentials de façon cohérente et stricte
  • Confirmer que les endpoints sensibles restent protégés même si CORS n’est pas pris en compte

Intégrer les protections associées, sans les contredire

CORS fait partie d’un ensemble. Il est utile de le relier à plusieurs mécanismes de défense, parce que la sécurité efficace est rarement un simple en-tête isolé.

Authentification et contrôle d’accès côté WordPress

C’est le socle. Si vos endpoints sont protégés correctement, CORS ne peut qu’ajouter de la friction pour les navigateurs non autorisés. À l’inverse, si vos endpoints sont trop ouverts, CORS peut vous donner une fausse impression de sécurité.

Sur WordPress, la protection repose sur des vérifications de rôles, des permissions, et des mécanismes de vérification de requêtes quand c’est pertinent. Pour des endpoints custom, ça implique souvent de relire la logique d’autorisation avant d’ajouter quoi que ce soit côté HTTP.

Limitation de surface : masquer ou réduire ce qui est exposé

Beaucoup de gens gèrent WordPress comme si toutes les URLs de l’écosystème devaient rester accessibles à tous. Or, plus vous exposez, plus la configuration CORS devient un problème à gérer.

Sans aller dans des techniques “agressives”, vous pouvez :

  • restreindre l’exposition de certaines routes si votre front ne les utilise pas
  • désactiver des endpoints inutiles (selon vos besoins et votre stack)
  • vérifier que les endpoints non utilisés ne répondent pas avec des informations trop détaillées

WAF et filtrage applicatif

Un WAF ou un filtrage applicatif peut bloquer des patterns d’attaques, mais attention aux interactions. Si vous introduisez des en-têtes CORS ou des en-têtes d’authentification que le WAF interprète comme suspects, vous pouvez générer des faux positifs. L’inverse est aussi vrai : si vous mettez trop de confiance, vous pouvez laisser passer des requêtes anormales.

Le bon réflexe est de tester les appels cross-origin dans les mêmes conditions que la prod réelle, avec les mêmes règles de filtrage, pas uniquement sur un environnement “nu”.

Deux erreurs qui reviennent presque à chaque projet

Quand je regarde les retours de déploiements, il y a deux erreurs qui reviennent avec une régularité étonnante, parce qu’elles semblent “pratiques” au départ.

1) Access-Control-Allow-Origin: * avec des routes qui exposent des données sensibles

Ça “marche” très bien pour du contenu public, mais ça peut devenir un problème dès que des informations privées, des états utilisateur, ou des comportements spécifiques sont impliqués. Même si l’auth est requise, certaines réponses peuvent révéler plus que prévu.

Le correctif consiste à remplacer le wildcard par une origine explicite, et à cadrer les routes.

2) Activation de credentials sans discipline sur l’origine

Dès que vous activez Access-Control-Allow-Credentials: true, vous devez faire preuve de rigueur. Une origine non maîtrisée, ou une logique dynamique mal écrite, peut créer des scénarios compliqués, notamment si des mises en cache ou des proxys entrent dans l’équation.

Voici les erreurs typiques que je corrige en premier, parce qu’elles expliquent une grande partie des incidents :

  • Appliquer CORS “global” sur tout WordPress au lieu de cibler les endpoints nécessaires
  • Oublier les en-têtes CORS sur OPTIONS, ce qui bloque les requêtes avec headers non simples
  • Autoriser des headers ou méthodes plus larges que requis, ce qui rend les attaques plus faciles
  • Laisser des réponses cachées par un proxy sans Vary: Origin quand la réponse dépend de l’origine
  • Confondre “lire depuis le navigateur” avec “autoriser l’action” côté WordPress

Exemple d’ajustement réel, sans magie : quand le navigateur bloque mais le serveur répond

Sur un projet avec un front séparé, le serveur répondait 200, mais le navigateur affichait une erreur CORS. Le développeur pensait que le serveur “faisait déjà CORS”, et c’était presque vrai.

Le problème était simple : les requêtes réelles avaient bien les en-têtes, mais les requêtes OPTIONS n’étaient pas traitées de manière cohérente par le composant situé devant WordPress (un https://gardewp.fr/securite-wordpress/ reverse proxy). Le résultat était frustrant : côté serveur, tout semblait normal, mais côté navigateur, le prévol échouait, donc le navigateur n’exposait jamais la réponse.

La correction a consisté à aligner les en-têtes sur OPTIONS et à tester de bout en bout, en tenant compte de l’URL exacte, et pas seulement du domaine. Une partie des chemins passait par un autre handler, et c’est ce détail qui avait cassé la “cohérence” CORS.

Cet exemple illustre une règle : quand vous corrigez CORS, vérifiez le chemin complet, du front au composant qui renvoie effectivement la réponse. WordPress n’est pas seul, et en prod, ce qui compte est la réponse finale vue par le navigateur.

Mise en œuvre WordPress : plugin, headers serveur, ou code applicatif

Il existe plusieurs manières de mettre CORS en place pour WordPress. Je reste volontairement général, parce que le bon choix dépend de votre infrastructure.

  • Si vous contrôlez le reverse proxy, c’est souvent plus simple de gérer les en-têtes à ce niveau, en ciblant les routes.
  • Si vous voulez un contrôle applicatif, un plugin peut ajouter des en-têtes sur certaines réponses WordPress.
  • Si vous développez des endpoints custom, il est parfois logique d’ajouter la logique CORS au niveau de l’endpoint, pour que la règle suive l’action.

Le risque, dans tous les cas, c’est le double ajout ou la contradiction. Par exemple, WordPress peut ajouter des en-têtes, puis le proxy peut les remplacer, ou l’inverse. Dans ces situations, le navigateur peut recevoir des en-têtes incohérents selon le chemin, et vous aurez des bugs qui n’apparaissent que sur certains écrans.

Une méthode efficace consiste à observer, pour chaque endpoint concerné, la réponse exacte que le navigateur reçoit. Si les en-têtes changent d’un chemin à l’autre sans raison claire, c’est souvent le signal d’une configuration partagée ou fragmentée.

Cas particuliers : plusieurs domaines front, environnements de test, et caches

Les environnements changent vite : staging, préprod, production. Et les frontends changent encore plus vite : parfois plusieurs domaines, parfois des sous-domaines.

La tentation est d’ajouter une règle “regex” qui couvre tous les sous-domaines, par exemple https://*.exemple.com. C’est parfois acceptable dans un contexte strict, mais c’est un choix de risque. Le mieux est de limiter à ce qui est vraiment contrôlé.

Si vous devez gérer plusieurs origines, faites-le avec une liste claire, et assurez-vous que votre mécanisme n’est pas trompé par des caractères, des encodages, ou des différences de protocole (http versus https). J’ai déjà vu des comportements où une origine en http finissait par être acceptée par erreur, ce qui donnait un faux sentiment de protection.

Enfin, les caches peuvent aggraver des erreurs. Si vous appliquez CORS à une réponse qui est mise en cache, mais sans mécanisme qui prend en compte l’origine, alors une réponse créée pour une origine peut être servie à une autre. D’où l’importance de Vary: Origin quand la réponse dépend de l’origine.

Check rapide avant de valider en production

Vous n’avez pas besoin de tout automatiser, mais vous avez besoin de discipline. Avant de déployer, assurez-vous de pouvoir expliquer, en une minute, pourquoi l’ouverture CORS est limitée et comment vos endpoints vérifient l’autorisation.

Vous devriez aussi pouvoir répondre à ces questions :

  • Quel domaine front est autorisé, exactement ?
  • Quelles routes sont concernées, et quelles routes ne le sont pas ?
  • Les requêtes OPTIONS passent-elles dans tous les chemins (incluant proxy et cache) ?
  • Est-ce que l’authentification et les permissions existent côté WordPress, indépendamment de CORS ?

Si vous pouvez répondre avec précision, vous avez déjà fait la moitié du travail.

Ce que je viserais comme “bonne posture” CORS sur WordPress

Une bonne posture CORS sur WordPress ressemble à un contrat clair entre votre front et votre back.

  • L’ouverture est minimale, une origine autorisée à la fois, et seulement sur les endpoints qui doivent l’être.
  • Les méthodes et headers autorisés suivent l’usage réel, pas un “tout pour éviter les blocages”.
  • Les prévols OPTIONS sont traités correctement, sinon le navigateur coupe court.
  • Les actions restent protégées côté WordPress, rôles et validation, pas seulement via un en-tête CORS.
  • Les caches et proxys ne peuvent pas mélanger les réponses entre origines.

CORS devient alors un outil utile, pas un pansement. Et dans la sécurité WordPress, ce genre de sobriété compte autant que la technique.

Si vous me décrivez votre architecture (domaine du front, si cookies sont nécessaires, si vous utilisez REST API et quels endpoints, et si un reverse proxy ou un CDN est en jeu), je peux vous proposer une politique CORS cible, avec les contrôles à faire pour valider sans ouvrir trop large.

Public Last updated: 2026-08-13 09:29:59 PM