11

我公司正在开发的 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 的内置网络服务器。响应与我们的登台服务器不同,即使请求完全相同,除了主机:

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);

这具有相同的行为,直到我们将响应代码修改204200.

看着身体

响应正文是空的,不应该包含任何内容,但是当我们向响应正文添加内容时,标题看起来好像没有任何问题。

因此,如果我添加正文内容,则会出现标题。没有正文内容?没有 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 "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 )

暂存的 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 vhost 正在开发中:

<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

4

3 回答 3

2

From everything I read here, including your comments it seems your "production" server is behind a PROXY more exactly CloudFlare. You have given details about your working development envinroment, but nothing about the non-working production environment.

Your setup seems correct, and if it does work on a development setup without a PROXY, it means that the PROXY is modifying the headers.

A quick search about this regarding CloudFlare has given enough indication that CloudFlare can be the cause of your problem.

I strongly suggest you enable "Development Mode" in CloudFlare so it will bypass the cache and you can see everything coming/going to the origin server.

The following article should help you understand and resolve your issue:

https://support.cloudflare.com/hc/en-us/articles/203063414-Why-can-t-I-see-my-CORS-headers-

UPDATE:

It appears that your issue is from Apache Mod Pagespeed, by turning it off your headers are present all times.

It is still unclear why the mod is stripping your headers, but that's for another question and time.

于 2018-08-30T20:29:57.737 回答
1

Your configuration makes it clear that the headers do get generated, so it's not the code or the middleware's fault.

I believe that the headers get removed by something - check Apache's mod_headers and configuration in case there's a rogue unset directive.

Another, less likely possibility is that you're looking at the staging server through a load balancer or proxy of some kind, which rewrites the headers and leaves the CORS out (to verify that, you might need to intercept Apache's outgoing traffic).

I have made both mistakes, myself.

于 2018-08-28T17:13:41.190 回答
0

请确保您在 Zend Expressive 中有正确的配置。例如,下面的代码将允许 CORS 访问任何调用域

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"]
}
]));
于 2018-08-25T17:01:02.083 回答