Отсутствие заголовков CORS в ответ от PHP/Apache2
Проект 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 и 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"
Конфигурация 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 [email protected]****.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
Ответы
Ответ 1
Из всего, что я читаю здесь, включая ваши комментарии, кажется, что ваш "производственный" сервер стоит за PROXY, точнее, CloudFlare. Вы подробно рассказали о своем развитии, но ничего не знаете о нерабочей производственной среде.
Ваша настройка кажется правильной, и если она работает над установкой разработки без PROXY, это означает, что PROXY изменяет заголовки.
Быстрый поиск об этом в отношении CloudFlare дал достаточно информации о том, что CloudFlare может стать причиной вашей проблемы.
Я настоятельно рекомендую вам включить "Режим разработки" в CloudFlare, чтобы он обходил кеш, и вы можете видеть все, что приходит/идет на исходный сервер.
Следующая статья должна помочь вам понять и решить вашу проблему:
https://support.cloudflare.com/hc/en-us/articles/203063414-Why-can-tI-see-my-CORS-headers-
ОБНОВИТЬ:
Похоже, что ваша проблема связана с Apache Mod Pagespeed, отключив ее, чтобы ваши заголовки присутствовали все время.
Пока неясно, почему мода снимает заголовки, но это касается другого вопроса и времени.
Ответ 2
Ваша конфигурация дает понять, что заголовки действительно генерируются, поэтому это не ошибка кода или промежуточного ПО.
Я считаю, что заголовки удаляются чем-то - проверьте Apache mod_headers
и конфигурацию, если есть директива mod_headers
unset
.
Другая, менее вероятная возможность заключается в том, что вы смотрите на промежуточный сервер через балансировщик нагрузки или какой-либо прокси-сервер, который переписывает заголовки и оставляет CORS (чтобы убедиться, что вам может потребоваться перехват исходящего трафика Apache).
Я сделал обе ошибки, сам.
Ответ 3
Убедитесь, что у вас есть правильная конфигурация в 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"]
}
]));