Проект 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);

Это будет происходить так же, пока мы не изменим код ответа на 204 или на что-либо другое, кроме 200.

Смотреть на тело

Тело ответа пустое и не должно содержать ничего, но когда мы добавляем содержимое в тело ответа, заголовки появляются так, как будто ничего не случилось.

Поэтому, если я добавлю содержимое тела, заголовки будут присутствовать. Нет содержимого тела? Нет заголовков CORS. Это какая-то настройка в Apache? Я пропускаю некоторые настройки в PHP? Я забыл что-нибудь?

Дальнейшие подробности

Все запросы были проверены с помощью httpie, Postman, curl и http-клиента PhpStorm.

Вот пример 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"

Конфигурация Cors в pipe.php (подстановочный знак только для тестирования):

$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 vhost на постановке:

<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

11
halfpastfour.am 20 Авг 2018 в 18:48

3 ответа

Лучший ответ

Из всего, что я прочитал здесь, включая ваши комментарии, кажется, что ваш «рабочий» сервер стоит за PROXY, а точнее CloudFlare. Вы подробно рассказали о своей рабочей среде разработки, но ничего о нерабочей производственной среде.

Ваша настройка кажется правильной, и если она работает с настройкой разработки без PROXY, это означает, что PROXY изменяет заголовки.

Быстрый поиск по этому поводу относительно CloudFlare дал достаточно признаков того, что CloudFlare может быть причиной вашей проблемы.

Я настоятельно рекомендую вам включить «Режим разработки» в CloudFlare, чтобы он обходил кеш и вы могли видеть все, что поступает / отправляется на исходный сервер.

Следующая статья должна помочь вам понять и решить вашу проблему:

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

ОБНОВЛЕНИЕ:

Похоже, что ваша проблема из Apache Mod Pagespeed, отключив его, ваши заголовки присутствуют все время.

До сих пор неясно, почему мод удаляет ваши заголовки, но это для другого вопроса и времени.

2
Norbert Boros 31 Авг 2018 в 15:27

Пожалуйста, убедитесь, что у вас есть правильная конфигурация в 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"]
}
]));
0
Rinsad Ahmed 26 Авг 2018 в 20:45

Ваша конфигурация проясняет, что заголовки действительно генерируются, так что это не код или ошибка промежуточного программного обеспечения.

Я считаю, что заголовки могут быть удалены чем-то - проверьте mod_headers и настройку Apache на случай, если существует неконтролируемая директива unset.

Другая, менее вероятная возможность состоит в том, что вы просматриваете промежуточный сервер через балансировщик нагрузки или какой-либо прокси-сервер, который переписывает заголовки и оставляет CORS вне (чтобы убедиться, что вам может потребоваться перехватить исходящий трафик Apache).

Я сам сделал обе ошибки.

1
LSerni 28 Авг 2018 в 17:13
51934338