Apache2 代理背后的 Symfony 5.3 有意外行为(在 ProxyPass/ProxyPassReverse 的浏览器中不正确 url)
Symfony 5.3 behind Apache2 proxy has unexpected behavior (incorrect url in browser with ProxyPass/ProxyPassReverse)
我想 运行 Apache 反向代理后面的 Symfony 5.3 应用程序,但它没有按预期工作
我在 Apache 反向代理后面有很多 Web 项目 Apache 服务器没有任何问题,但是这个让我无法接受:-/
场景:
设置
浏览器
代理服务器
应用服务器
设置:
http://host/app1/login
ProxyPass /app1/ http://192.168.1.1/
ProxyPassReverse /app1/ http://192.168.1.1/
http://192.168.1.1/login
我希望设置做什么:
流量
浏览器
代理服务器
应用服务器
浏览器 > 代理 > 应用服务器
浏览器请求 url
(例如 post 凭据)
/app1/login
代理将请求映射到应用
/login
服务器在 /login
控制器
中执行操作(并重定向到 /success
见下图)
流量
应用服务器
代理服务器
浏览器
应用服务器 > 代理 > 浏览器
应用服务器发送
/success
重定向
代理映射响应
/app1/success
收到重定向指向/app1/success
现实生活中发生的事情:
流量
浏览器
代理服务器
应用服务器
浏览器 > 代理 > 应用服务器
浏览器请求 url
(例如 post 凭据)
/app1/login
代理将请求映射到应用
/login
服务器在 /login
控制器
中执行操作(并重定向到 /success
见下图)
流量
应用服务器
代理服务器
浏览器
应用服务器 > 代理 > 浏览器
应用服务器发送
/success
重定向
### 错误###
### 错误###
### 错误 ###
代理不知何故不重写
/app1/success
而是去 /success
接收重定向 指向 /success
并调用 http://host/success
并转到 404
(或更糟糕的东西)
我想要什么
Browser: Hey i post credentials to /app1/login
Proxy: Uhh, one moment.. that has to be /login - otherwise Server does not know
Server: Yo, /login seems good, go to /success, oh and load /this/css.file
Proxy: Err, just a second.. that has to be /app1/success aaand /app1/this/css.file - otherwise Browser get's confused
Browser: Yay, ill make my way to /app1/success
Browser: Whoa, what a nice design in /app1/this/css.file
大家开心!
让事情变得更糟
- 我只能控制 network/server 的东西,而不是 Symfony 应用程序本身
- 所以,我无法更改应用程序 code/configuration 本身,我只需要“托管”这个东西
我已经(字面上)阅读了数百篇关于 this/similar 问题的 Stack post。
我已经阅读了 Apache、ModProxy、Symfony 的“所有”文档,并花了几天时间 Google。
现在希望得到这个运行宁很低,脑袋疼
问题
- 你需要什么额外的信息来解决我的问题
- 到底是什么(可能很明显地隐藏在众目睽睽之下)我缺少一些配置内容??
- 即使解决方案在 Symfony 应用程序内部并且维护者必须修复它(<顺便说一句,这使它成为一个有效的 Whosebug 问题;))
附加信息
已安装服务器:
- Ubuntu 20.04
- 阿帕奇 2.4
- Php 7.4 (FPM)
Server/App 配置:
- vHost 代理服务器(相关部分)
ProxyRequests Off
ProxyVia Off
ProxyPreserveHost On
ProxyTimeout 1200
ProxyReceiveBufferSize 4096
<VirtualHost *:80>
ProxyPass /app1/ http://192.168.1.1/
ProxyPassReverse /app1/ http://192.168.1.1/
</VirtualHost>
- vHost App-Server(相关部分)
<VirtualHost *:80>
DocumentRoot /var/www/public
<Directory /var/www/public>
AllowOverride All
</Directory>
</VirtualHost>
- htaccess 应用服务器(重写部分)
# Determine the RewriteBase automatically and set it as environment variable.
# If you are using Apache aliases to do mass virtual hosting or installed the
# project in a subdirectory, the base path will be prepended to allow proper
# resolution of the index.php file and to redirect to the correct URI. It will
# work in environments without path prefix as well, providing a safe, one-size
# fits all solution. But as you do not need it in this case, you can comment
# the following 2 lines to eliminate the overhead.
RewriteCond %{REQUEST_URI}::[=14=] ^(/.+)/(.*)::$
RewriteRule .* - [E=BASE:%1]
# Sets the HTTP_AUTHORIZATION header removed by Apache
RewriteCond %{HTTP:Authorization} .+
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
# Redirect to URI without front controller to prevent duplicate content
# (with and without `/index.php`). Only do this redirect on the initial
# rewrite by Apache and not on subsequent cycles. Otherwise we would get an
# endless redirect loop (request -> rewrite to front controller ->
# redirect -> request -> ...).
# So in case you get a "too many redirects" error or you always get redirected
# to the start page because your Apache does not expose the REDIRECT_STATUS
# environment variable, you have 2 choices:
# - disable this feature by commenting the following 2 lines or
# - use Apache >= 2.3.9 and replace all L flags by END flags and remove the
# following RewriteCond (best solution)
RewriteCond %{ENV:REDIRECT_STATUS} =""
RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/ [R=301,L]
# If the requested filename exists, simply serve it.
# We only want to let Apache serve files and not directories.
# Rewrite all other queries to the front controller.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ %{ENV:BASE}/index.php [L]
我终于让它工作了。
代理配置从这里更改:
<VirtualHost *:80>
ProxyPass /app1/ http://192.168.1.1/
ProxyPassReverse /app1/ http://192.168.1.1/
</VirtualHost>
对此:
<VirtualHost *:80>
ProxyPass /app1/ http://192.168.1.1/app1/
ProxyPassReverse /app1/ http://192.168.1.1/app1/
</VirtualHost>
现在指向应用服务器上的/var/www/public/app1/目录,该目录不存在
所以..在应用程序服务器上我改变了这个:
<VirtualHost *:80>
DocumentRoot /var/www/public
<Directory /var/www/public>
AllowOverride All
</Directory>
</VirtualHost>
对此:
<VirtualHost *:80>
DocumentRoot /var/www/public
Alias /app1 /var/www/public
<Directory /var/www/public>
AllowOverride All
</Directory>
</VirtualHost>
.. 和 DING .. 它正在工作。
由于使用了 webpack 配置,因此需要进行一些额外的应用故障排除。必须进行更改才能在清单中集成 /app1/ 前缀。在构建时获得正确的 style/content url。 ;)
例如使用 Symfony Encore
来自这个:
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')
对此:
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/app1/build')
// only needed for CDN's or sub-directory deploy
.setManifestKeyPrefix('build/')
感谢阅读 :)
如果有什么我应该补充的以便更好地理解,请评论;)
我想 运行 Apache 反向代理后面的 Symfony 5.3 应用程序,但它没有按预期工作
我在 Apache 反向代理后面有很多 Web 项目 Apache 服务器没有任何问题,但是这个让我无法接受:-/
场景:
设置 | 浏览器 | 代理服务器 | 应用服务器 |
---|---|---|---|
设置: | http://host/app1/login |
ProxyPass /app1/ http://192.168.1.1/ ProxyPassReverse /app1/ http://192.168.1.1/ |
http://192.168.1.1/login |
我希望设置做什么:
流量 | 浏览器 | 代理服务器 | 应用服务器 |
---|---|---|---|
浏览器 > 代理 > 应用服务器 | 浏览器请求 url (例如 post 凭据) /app1/login |
代理将请求映射到应用 /login |
服务器在 /login 控制器 中执行操作(并重定向到 /success 见下图) |
流量 | 应用服务器 | 代理服务器 | 浏览器 |
---|---|---|---|
应用服务器 > 代理 > 浏览器 | 应用服务器发送/success 重定向 |
代理映射响应 /app1/success |
收到重定向指向/app1/success |
现实生活中发生的事情:
流量 | 浏览器 | 代理服务器 | 应用服务器 |
---|---|---|---|
浏览器 > 代理 > 应用服务器 | 浏览器请求 url (例如 post 凭据) /app1/login |
代理将请求映射到应用 /login |
服务器在 /login 控制器 中执行操作(并重定向到 /success 见下图) |
流量 | 应用服务器 | 代理服务器 | 浏览器 |
---|---|---|---|
应用服务器 > 代理 > 浏览器 | 应用服务器发送/success 重定向 |
### 错误### ### 错误### ### 错误 ### 代理不知何故不重写 /app1/success 而是去 /success |
接收重定向 指向 /success 并调用 http://host/success 并转到 404 (或更糟糕的东西) |
我想要什么
Browser: Hey i post credentials to /app1/login
Proxy: Uhh, one moment.. that has to be /login - otherwise Server does not know
Server: Yo, /login seems good, go to /success, oh and load /this/css.file
Proxy: Err, just a second.. that has to be /app1/success aaand /app1/this/css.file - otherwise Browser get's confused
Browser: Yay, ill make my way to /app1/success
Browser: Whoa, what a nice design in /app1/this/css.file
大家开心!
让事情变得更糟
- 我只能控制 network/server 的东西,而不是 Symfony 应用程序本身
- 所以,我无法更改应用程序 code/configuration 本身,我只需要“托管”这个东西
我已经(字面上)阅读了数百篇关于 this/similar 问题的 Stack post。
我已经阅读了 Apache、ModProxy、Symfony 的“所有”文档,并花了几天时间 Google。
现在希望得到这个运行宁很低,脑袋疼
问题
- 你需要什么额外的信息来解决我的问题
- 到底是什么(可能很明显地隐藏在众目睽睽之下)我缺少一些配置内容??
- 即使解决方案在 Symfony 应用程序内部并且维护者必须修复它(<顺便说一句,这使它成为一个有效的 Whosebug 问题;))
附加信息
已安装服务器:
- Ubuntu 20.04
- 阿帕奇 2.4
- Php 7.4 (FPM)
Server/App 配置:
- vHost 代理服务器(相关部分)
ProxyRequests Off
ProxyVia Off
ProxyPreserveHost On
ProxyTimeout 1200
ProxyReceiveBufferSize 4096
<VirtualHost *:80>
ProxyPass /app1/ http://192.168.1.1/
ProxyPassReverse /app1/ http://192.168.1.1/
</VirtualHost>
- vHost App-Server(相关部分)
<VirtualHost *:80>
DocumentRoot /var/www/public
<Directory /var/www/public>
AllowOverride All
</Directory>
</VirtualHost>
- htaccess 应用服务器(重写部分)
# Determine the RewriteBase automatically and set it as environment variable.
# If you are using Apache aliases to do mass virtual hosting or installed the
# project in a subdirectory, the base path will be prepended to allow proper
# resolution of the index.php file and to redirect to the correct URI. It will
# work in environments without path prefix as well, providing a safe, one-size
# fits all solution. But as you do not need it in this case, you can comment
# the following 2 lines to eliminate the overhead.
RewriteCond %{REQUEST_URI}::[=14=] ^(/.+)/(.*)::$
RewriteRule .* - [E=BASE:%1]
# Sets the HTTP_AUTHORIZATION header removed by Apache
RewriteCond %{HTTP:Authorization} .+
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
# Redirect to URI without front controller to prevent duplicate content
# (with and without `/index.php`). Only do this redirect on the initial
# rewrite by Apache and not on subsequent cycles. Otherwise we would get an
# endless redirect loop (request -> rewrite to front controller ->
# redirect -> request -> ...).
# So in case you get a "too many redirects" error or you always get redirected
# to the start page because your Apache does not expose the REDIRECT_STATUS
# environment variable, you have 2 choices:
# - disable this feature by commenting the following 2 lines or
# - use Apache >= 2.3.9 and replace all L flags by END flags and remove the
# following RewriteCond (best solution)
RewriteCond %{ENV:REDIRECT_STATUS} =""
RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/ [R=301,L]
# If the requested filename exists, simply serve it.
# We only want to let Apache serve files and not directories.
# Rewrite all other queries to the front controller.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ %{ENV:BASE}/index.php [L]
我终于让它工作了。
代理配置从这里更改:
<VirtualHost *:80>
ProxyPass /app1/ http://192.168.1.1/
ProxyPassReverse /app1/ http://192.168.1.1/
</VirtualHost>
对此:
<VirtualHost *:80>
ProxyPass /app1/ http://192.168.1.1/app1/
ProxyPassReverse /app1/ http://192.168.1.1/app1/
</VirtualHost>
现在指向应用服务器上的/var/www/public/app1/目录,该目录不存在
所以..在应用程序服务器上我改变了这个:
<VirtualHost *:80>
DocumentRoot /var/www/public
<Directory /var/www/public>
AllowOverride All
</Directory>
</VirtualHost>
对此:
<VirtualHost *:80>
DocumentRoot /var/www/public
Alias /app1 /var/www/public
<Directory /var/www/public>
AllowOverride All
</Directory>
</VirtualHost>
.. 和 DING .. 它正在工作。
由于使用了 webpack 配置,因此需要进行一些额外的应用故障排除。必须进行更改才能在清单中集成 /app1/ 前缀。在构建时获得正确的 style/content url。 ;)
例如使用 Symfony Encore
来自这个:
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')
对此:
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/app1/build')
// only needed for CDN's or sub-directory deploy
.setManifestKeyPrefix('build/')
感谢阅读 :)
如果有什么我应该补充的以便更好地理解,请评论;)