普通网友 2018-08-20 15:48
浏览 226
已采纳

PHP / Apache2响应中缺少CORS头

A Zend Expressive project my company is working on is ready to be shipped but in our staging environment we seem to be missing response headers for a CORS pre-flight request. This does not happen in our development environment. We're using CorsMiddleware in our pipeline but it doesn't look like that middleware is the culprit.

The problem

During runtime, the middleware detects incoming pre-flight requests and it will reply with a response like so:

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

Well, that only works on our development servers and php's built-in webservers. The response is different from our staging server, even though the request is exactly the same, apart from the host:

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

What we've tried

Investigating the middleware

We've verified that CorsMiddleware runs perfectly fine and actually sets the required headers. When we modify CorsMiddleware's response code and set it to 202 instead of 200 we now do get the headers we're looking for. Changing the response code back to 200 makes the headers disappear again.

Setting the headers manually

Using the following example:

header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Headers: content-type');
header('Vary: Origin');
exit(0);

This has the same behavior until we modify the response code to 204 or anything other than 200.

Looking at the body

The response body is empty and shouldn't contain anything but when we add content to the response body the headers appear as if nothing was wrong.

So if I add body content, the headers are present. No body content? No CORS headers. Is this some setting in Apache? Am I missing some configuration in PHP? Am I forgetting anything?

Further details

All requests have been tested with httpie, Postman, curl and PhpStorm's http client.

Here's the httpie example:

http -v OPTIONS https://staging.****.com \
    'access-control-request-method:POST' \
    'origin:https://example.com' \
    'access-control-request-headers:content-type'

Here's the curl example:

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"

Cors configuration in pipeline.php (wildcard only for testing):

$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));
    },
]);

The staging environment:

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 vhost on staging:

<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 on development:

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

To everybody pointing fingers to Cloudflare:

Try this direct link with httpie. This link is not using 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'

Check the source code in your browser: http://37.97.135.33/cors.php?source=1

  • 写回答

3条回答 默认 最新

  • duanbi3385 2018-08-30 20:29
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥30 Hyper-v虚拟机相关问题,求解答。
  • ¥15 TSM320F2808PZA芯片 Bootloader
  • ¥45 谷歌浏览器出现开发者工具无法显示已创建的,但您可以调试已部署的代码。 状态代码 404, net::ERR HTTP RESPONSE CODE FAILURE
  • ¥15 如何解决蓝牙通话音频突发失真问题
  • ¥15 安装opengauss数据库报错
  • ¥15 【急】在线问答CNC雕刻机的电子电路与编程
  • ¥60 在mc68335芯片上移植ucos ii 的成功工程文件
  • ¥15 笔记本外接显示器正常,但是笔记本屏幕黑屏
  • ¥15 Python pandas
  • ¥15 蓝牙硬件,可以用哪几种方法控制手机点击和滑动