我的公司正在开发一个Zend Expressive项目,准备上线,但在我们的测试环境中,CORS预检请求似乎缺少响应头。这在我们的开发环境中不会发生。我们在管道中使用CorsMiddleware,但它看起来不是罪魁祸首。
问题:
在运行时,中间件检测到传入的预检请求,并将回复如下响应:
问题:
在运行时,中间件检测到传入的预检请求,并将回复如下响应:
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
好的,这只适用于我们的开发服务器和php内置的Web服务器。尽管请求完全相同,除了主机名之外,但响应与我们的暂存服务器不同:
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
我们所尝试的
调查中间件
我们已经验证了CorsMiddleware运行得非常好,并且实际上设置了所需的标头。当我们修改CorsMiddleware的响应代码并将其设置为202
而不是200
时,我们现在确实获得了我们正在寻找的标头。将响应代码更改回200
会使标头再次消失。
手动设置标头
使用以下示例:
header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Headers: content-type');
header('Vary: Origin');
exit(0);
在将响应代码修改为204
或任何不是200
的值之前,它的行为相同。
查看正文
响应正文为空,不应包含任何内容,但是当我们向响应正文添加内容时,标头看起来好像没有问题。
因此,如果我添加正文内容,则存在标头。没有正文内容?没有CORS标头。这是Apache中的某些设置吗?我是否缺少PHP中的某些配置?我有什么遗漏吗?
进一步细节
所有请求均已使用httpie、Postman、curl和PhpStorm的http客户端进行测试。
以下是httpie示例:
http -v OPTIONS https://staging.****.com \
'access-control-request-method:POST' \
'origin:https://example.com' \
'access-control-request-headers:content-type'
以下是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"
pipeline.php中的Cors配置(仅用于测试的通配符):
$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 )
在staging上配置Apache2虚拟主机:
<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>
在开发中使用的Apache2虚拟主机:
<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>
对于指责Cloudflare的所有人:
请尝试使用httpie直接访问此链接。该链接未使用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'
在浏览器中检查源代码:http://37.97.135.33/cors.php?source=1
mod_headers
设置,以了解暂存服务器的情况。它很可能被配置为剥离“不安全”的头部信息,通常是像x-powered-by
这样的内容,从你的结果来看似乎是这种情况。如果暂存服务器在代理后面运行,也有可能配置上游代理执行相同的操作。 - Crisp