Un projet Zend Expressive sur lequel travaille mon entreprise est prêt à être expédié, mais dans notre environnement de transfert, nous manquons d'en-têtes de réponse pour une demande de pré-vol auprès de la base de données CORS. Cela ne se produit pas dans notre environnement de développement. Nous utilisons CorsMiddleware dans notre pipeline, mais il ne semble pas que le middleware soit le coupable.
Pendant l'exécution, le middleware détecte les demandes de pré-vol entrantes et répondra avec une réponse comme ceci:
HTTP/1.1 200 OK
Date: Mon, 20 Aug 2018 15:09:03 GMT
Server: Apache
X-Powered-By: PHP/7.1.19
Access-Control-Allow-Origin: https://example.com
Vary: Origin
Access-Control-Allow-Headers: content-type
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
Eh bien, cela ne fonctionne que sur nos serveurs de développement et les serveurs Web intégrés de php. La réponse est différente de notre serveur de transfert, même si la demande est exactement la même, à l'exception de l'hôte:
HTTP/1.1 200 OK
Date: Mon, 20 Aug 2018 15:11:29 GMT
Server: Apache
Keep-Alive: timeout=5, max=100
Cache-Control: max-age=0, no-cache
Content-Length: 0
Content-Type: text/html; charset=UTF-8
Nous avons vérifié que CorsMiddleware fonctionne parfaitement et définit les en-têtes requis. Lorsque nous modifions le code de réponse de CorsMiddleware et le définissons sur 202
au lieu de 200
, nous do obtenons maintenant les en-têtes que nous recherchons. Si vous remettez le code de réponse en 200
, les en-têtes disparaissent à nouveau.
En utilisant l'exemple suivant:
header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Headers: content-type');
header('Vary: Origin');
exit(0);
Cela a le même comportement jusqu'à ce que nous modifions le code de réponse en 204
ou tout autre chose que 200
.
Le corps de la réponse est vide et ne doit contenir aucun élément, mais lorsque nous ajoutons du contenu au corps de la réponse, les en-têtes apparaissent comme si rien n'était faux.
Donc, si j'ajoute du contenu corporel, les en-têtes sont présents. Pas de contenu corporel? Pas d'en-tête CORS. Est-ce une configuration dans Apache? Est-ce qu'il me manque une configuration en PHP? Est-ce que j'oublie quelque chose?
Toutes les demandes ont été testées avec httpie, Postman, curl et le client http de PhpStorm.
Voici l'exemple httpie:
http -v OPTIONS https://staging.****.com \
'access-control-request-method:POST' \
'Origin:https://example.com' \
'access-control-request-headers:content-type'
Voici l'exemple de curl:
curl "https://staging.****.com" \
--request OPTIONS \
--include \
--header "access-control-request-method: POST" \
--header "Origin: https://example.com" \
--header "access-control-request-headers: content-type"
$app->pipe(new CorsMiddleware([
"Origin" => [
"*",
],
"headers.allow" => ['Content-Type'],
"headers.expose" => [],
"credentials" => false,
"cache" => 0,
// Get list of allowed methods from matched route or provide empty array.
'methods' => function (ServerRequestInterface $request) {
$result = $request->getAttribute(RouteResult::class);
/** @var \Zend\Expressive\Router\Route $route */
$route = $result->getMatchedRoute();
return $route ? $route->getAllowedMethods() : [];
},
// Respond with a json response containing the error message when the CORS check fails.
'error' => function (
ServerRequest $request,
Response $response,
$arguments
) {
$data['status'] = 'error';
$data['message'] = $arguments['message'];
return $response->withHeader('Content-Type', 'application/json')
->getBody()->write(json_encode($data));
},
]);
OS: Debian 9.5 server
Webserver: Apache/2.4.25 (Debian) (built: 2018-06-02T08:01:13)
PHP: PHP 7.1.20-1+0~20180725103315.2+stretch~1.gbpd5b650 (cli) (built: Jul 25 2018 10:33:20) ( NTS )
<IfModule mod_ssl.c>
<VirtualHost ****:443>
ServerName staging.****.com
DocumentRoot /var/www/com.****.staging/public
ErrorLog /var/log/Apache2/com.****.staging.error.log
CustomLog /var/log/Apache2/com.****.staging.access.log combined
<Directory /var/www/com.****.staging>
Options +SymLinksIfOwnerMatch
AllowOverride All
Order allow,deny
allow from all
</Directory>
SSLCertificateFile /etc/letsencrypt/live/staging.****.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/staging.****.com/privkey.pem
Include /etc/letsencrypt/options-ssl-Apache.conf
</VirtualHost>
</IfModule>
<VirtualHost *:443>
ServerName php71.****.com
ServerAdmin dev@****.com
DocumentRoot /var/www/
<Directory /var/www/>
Options Indexes FollowSymlinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${Apache_LOG_DIR}/error.ssl.log
CustomLog ${Apache_LOG_DIR}/access.ssl.log combined
SSLEngine On
SSLCertificateFile /etc/ssl/certs/****.crt
SSLCertificateKeyFile /etc/ssl/certs/****.key
</VirtualHost>
Essayez ce lien direct avec httpie. Ce lien n'utilise pas cloudflare:
http -v OPTIONS http://37.97.135.33/cors.php \
'access-control-request-method:POST' \
'Origin:https://example.com' \
'access-control-request-headers:content-type'
Vérifiez le code source dans votre navigateur: http://37.97.135.33/cors.php?source=1
D'après tout ce que j'ai lu ici, y compris vos commentaires, il semble que votre serveur "de production" est derrière un PROXY plus exactement CloudFlare. Vous avez fourni des détails sur votre environnement de développement actif, mais rien sur l'environnement de production non opérationnel.
Votre configuration semble correcte, et si cela fonctionne sur une configuration de développement sans un PROXY, cela signifie que le PROXY est en train de modifier les en-têtes.
Une recherche rapide à ce sujet concernant CloudFlare a donné suffisamment d'indications sur le fait que CloudFlare peut être la cause de votre problème.
Je vous suggère fortement d'activer le "mode de développement" dans CloudFlare afin qu'il contourne le cache et que vous puissiez voir tout ce qui va/vient sur le serveur Origin.
L'article suivant devrait vous aider à comprendre et à résoudre votre problème:
https://support.cloudflare.com/hc/en-us/articles/203063414-Why-can-t-I-see-my-CORS-headers-
METTRE À JOUR:
Il semble que votre problème vienne d'Apache Mod Pagespeed. En le désactivant, vos en-têtes sont présents à tout moment.
On ne sait toujours pas pourquoi le mod enlève vos en-têtes, mais c'est pour une autre question et une autre fois.
Votre configuration indique clairement que les en-têtes sont générés. Ce n'est donc pas le code ni le middleware.
Je crois que les en-têtes sont supprimés par quelque chose - vérifiez le mod_headers
et la configuration d’Apache au cas où il y aurait une directive unset
non autorisée.
Une autre possibilité moins probable est que vous examiniez le serveur intermédiaire via un équilibreur de charge ou un proxy quelconque, qui réécrit les en-têtes et laisse le CORS sorti (pour vérifier cela, vous devrez peut-être intercepter le trafic sortant d'Apache).
J'ai fait les deux erreurs, moi-même.
Assurez-vous d'avoir la bonne configuration dans Zend Expressive. Par exemple, le code ci-dessous permettra à CORS d'accéder à tout domaine appelant.
use Psr\Http\Message\ServerRequestInterface;
use Tuupola\Middleware\CorsMiddleware;
use Zend\Expressive\Router\RouteResult;
$app->pipe(new CorsMiddleware([
"Origin" => ["*"],
"methods" => ["GET", "POST", "PUT", "PATCH", "DELETE"]
}
]));