如何使用 Apache 正确配置反向代理,以用于跨域 AJAX?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14186045/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 10:54:30  来源:igfitidea点击:

How to correctly configure a reverse proxy with Apache, to be used for cross-domain AJAX?

ajaxapachecross-domainwampserver

提问by Andrei Oniga

Needing to develop a web application that at the same time is highly dependent on an API but at the same time cannot reside on the same domain as the API itself, it's been quite tricky getting around the "Same Origin Policy" when making asynchronous HTTP requests (AJAX). At one point, I was recommended to install WAMP on my computer (running Windows 7) and to configure a reverse proxy with Apache. The same person gave me the Apache directives bellow that I added to the httpd.conffile, after telling me to create an alias for the IP 127.0.0.1 named dev, within the file at c:\windows\system32\drivers\etc\hosts(which I did):

需要开发一个 Web 应用程序,该应用程序同时高度依赖 API 但同时不能与 API 本身驻留在同一域中,在发出异步 HTTP 请求时绕过“同源策略”非常棘手(阿贾克斯)。有一次,有人建议我在我的计算机上安装 WAMP(运行 Windows 7)并使用 Apache 配置反向代理。同样的人给我的Apache指令吼叫,我添加到httpd.conf文件,告诉我为IP 127.0.0.1命名创建别名后开发,文件内的c:\windows\system32\drivers\etc\hosts(我做了):

LoadModule headers_module modules/mod_headers.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule ssl_module modules/mod_ssl.so

Listen 127.0.0.1:8080
ProxyRequests off

<Proxy *>
                Order deny,allow
                Deny from all
                Allow from 127.0.0.1
</Proxy>

<VirtualHost dev:8080>
                ProxyPass / https://app.somesite.com:5002/
                ProxyPassReverse / https://app.somesitecom:5002/
                ProxyPassReverseCookieDomain app.somesite.com dev
                Header edit Location ^https://dev(:8080)?(.+)$ http://dev
                Header edit Set-Cookie "(^.+); secure; HttpOnly$" "; HttpOnly"
                SSLProxyEngine on
    SSLProxyVerify none
</VirtualHost>

Since I'm a complete novice when it comes to configuring servers, I simply pasted the directives and fortunately enough, the proxy worked. It returns the correct response from the API when I use the browser's address bar to access, for example, http://dev:8080/a/w/currencies.

由于我在配置服务器方面完全是新手,因此我只是粘贴了指令,幸运的是,代理工作正常。例如,当我使用浏览器的地址栏访问时,它会从 API 返回正确的响应http://dev:8080/a/w/currencies

Unfortunately though, an AJAX request to the same URL (code bellow) makes Chrome give me the XMLHttpRequest cannot load http://dev:8080/a/w/currencies. Origin http://dev is not allowed by Access-Control-Allow-Origin.error.

不幸的是,对同一 URL(代码如下)的 AJAX 请求使 Chrome 给我XMLHttpRequest cannot load http://dev:8080/a/w/currencies. Origin http://dev is not allowed by Access-Control-Allow-Origin.错误。

$.ajax({
    url: "http://dev:8080/a/w/currencies",
    type: "GET",
    dataType: "json",
    data: {

    },
    success: function(data){
        console.log(data);
    }
}); 

So what must still be done in order for this proxy to work with AJAX? I've been told something about an aliasdirective, but not specific and clear enough, so it didn't make much sense to my inexperienced brain.

那么,为了让这个代理与 AJAX 一起工作,还必须做什么?有人告诉我一些关于alias指令的事情,但不够具体和清晰,所以对我没有经验的大脑来说没有多大意义。

PS: Also, I've been told "the problem is that you're getting the files from dev:80 and ajaxing to dev:8080". Given my inexperience, neither this makes much sense.

PS:另外,有人告诉我“问题是你从 dev:80 获取文件并 ajaxing 到 dev:8080”。鉴于我的经验不足,这都没有多大意义。

回答by Registered User

You are having a server with a public IP and apache is running on it.Now you want to host your applications on LAN and also want them to be accessible on internet the important part is these applications are still running on the machines on LAN.

您拥有一台带有公共 IP 的服务器,并且 apache 正在其上运行。现在您想在 LAN 上托管您的应用程序,并希望它们可以在 Internet 上访问,重要的是这些应用程序仍在 LAN 上的机器上运行。

                           |--------------192.168.1.3
                           |            (internal3.example.com)
                           |
                           |--------------192.168.1.4
                           |            (internal4.example.com)
  (Public IP )             |
            A--------------|
(reverse proxy server)     |
  (192.168.1.25)           |
example.com                |
                           |--------------192.168.1.1
                           |            (internal1.example.com)
                           |
                           |--------------192.168.1.2
                           |            (internal2.example.com)

I am using Ubuntu to host Apache the vhost definition in case of Debian based systems the definiton of websites is done on

我正在使用 Ubuntu 来托管 Apache,在基于 Debian 的系统的情况下,网站的定义是在上面完成的

/etc/apache2/sites-enabled/*.conf

/etc/apache2/sites-enabled/*.conf

where *conf corresponds to

其中 *conf 对应于

internal1.conf internal2.conf internal3.conf internal4.conf

internal1.conf internal2.conf internal3.conf internal4.conf

The vhost definition of each of these sites will be as follows

每个站点的 vhost 定义如下

/etc/apache2/sites-enabled/internal1.example.conf

/etc/apache2/sites-enabled/internal1.example.conf

  ServerAdmin webmaster@localhost
  ServerName internal1.example.com
  ProxyRequests off
  <proxy *>
  Order deny,allow
  Allow from all
  </proxy >
  ProxyPass / http://192.168.1.1/
  ProxyPassReverse / http://192.168.1.1/ </VirtualHost >

  ServerAdmin webmaster@localhost
  ServerName internal1.example.com
  ProxyRequests off
  <proxy *>
  Order deny,allow
  Allow from all
  </proxy >
  ProxyPass / http://192.168.1.1/
  ProxyPassReverse / http://192.168.1.1/ </VirtualHost >

/etc/apache2/sites-enabled/internal2.example.conf

/etc/apache2/sites-enabled/internal2.example.conf

<virtualhost *:80>

      ServerAdmin webmaster@localhost
      ServerName internal2.example.com
      ProxyRequests off
      <proxy *>
      Order deny,allow
      Allow from all
      </proxy >
      ProxyPass / http://192.168.1.2/
      ProxyPassReverse / http://192.168.1.2/
</VirtualHost >

/etc/apache2/sites-enabled/internal3.example.conf

/etc/apache2/sites-enabled/internal3.example.conf

<virtualhost *:80>

      ServerAdmin webmaster@localhost
      ServerName internal3.example.com
      ProxyRequests off
      <proxy *>
      Order deny,allow
      Allow from all
      </proxy >
      ProxyPass / http://192.168.1.3/
      ProxyPassReverse / http://192.168.1.3/
</VirtualHost >

/etc/apache2/sites-enabled/internal4.example.conf

/etc/apache2/sites-enabled/internal4.example.conf

      ServerAdmin webmaster@localhost
      ServerName internal4.example.com
      ProxyRequests off
      <proxy *>
      Order deny,allow
      Allow from all
      </proxy >
      ProxyPass / http://192.168.1.4/
      ProxyPassReverse / http://192.168.1.4/
</VirtualHost >

Note in all of the above vhost definitions I have dropped the options of Log files. So if you apply to a production server add them in each of the vhost file. Above is just to give you a clear cut example as how it can be working. I run a very complex Apache setup so above is just a small example to help you.

请注意,在上述所有 vhost 定义中,我都删除了日志文件的选项。因此,如果您应用于生产服务器,请将它们添加到每个 vhost 文件中。以上只是给你一个清晰的例子,说明它是如何工作的。我运行了一个非常复杂的 Apache 设置,所以上面只是一个帮助你的小例子。

Now coming to Ajax part of your question

现在来到你问题的 Ajax 部分

in chrome press Ctrl+Shift+I you will see where exactly the application is broken, it will give you some clue, (issue the request from a machine different from the machine on which you are developing web application) also if you can look at apache logs if the request from http://samplepage which has ajx api actually reached your apache server that will give you more hint, if the proxy is forwarding your request correctly, post the HTTP HEADERS by using some tool in firefox like live_http in condition when there was no request and the condition when the request was made by the application that way observing the headers one can help you if the request reached the server behind the reverse proxy,also check the logs of server which is running reverse proxy if the request from web reached it or not and if the request reached what is the URL that was requested.This will give you a clue,

在 chrome 中按 Ctrl+Shift+I,您将看到应用程序的确切损坏位置,它会给您一些线索,(从与您正在开发 Web 应用程序的机器不同的机器发出请求),如果您也可以查看apache 记录请求是否来自http://sample具有 ajx api 的页面实际上到达了您的 apache 服务器,这将为您提供更多提示,如果代理正确转发您的请求,请在没有请求的情况下使用 firefox 中的一些工具(如 live_http)发布 HTTP 标头,以及请求是由应用程序以这种方式发出的,如果请求到达反向代理后面的服务器,则观察标题可以帮助您,如果来自 Web 的请求是否到达,请检查正在运行反向代理的服务器的日志以及请求到达请求的 URL 是什么。这会给你一个线索,

and for development purpose in your .conf files disable the rewrite rules for some time to test ,do it one by one.

并且出于开发目的,在您的 .conf 文件中禁用重写规则一段时间以进行测试,一一进行。

回答by user2977023

The problem here is that the browser is trying to protect you from getting pwned by random javascript placed on some web page. If it would let all javascript to run in the same context you would lose you Facebook sessions cookies or some other data to bad guys.

这里的问题是浏览器试图保护您免受放置在某个网页上的随机 javascript 的攻击。如果它让所有 javascript 在相同的上下文中运行,你就会失去 Facebook 会话 cookie 或其他一些数据给坏人。

In this case the culprit could be something so simple as Chrome does not consider 'dev' to be a Fully Qualified Domain Name so it will fail the same origin test. Other reason might be that at some point you are getting stuff from app.somesite.dev and at some point you send requests to 'dev'

在这种情况下,罪魁祸首可能很简单,因为 Chrome 不认为“dev”是完全合格的域名,因此它将无法通过同源测试。其他原因可能是在某些时候你从 app.somesite.dev 获取东西,在某些时候你向“dev”发送请求

The servers don't care what they send and it is the browser you need to fool to believe everything is coming from the same host

服务器不在乎他们发送什么,它是你需要愚弄的浏览器相信一切都来自同一个主机

  1. I would replace 'dev' in the hosts file with dev.example.com 127.0.0.1
  2. I would make sure everything coming out of the Apache proxy only refers to dev.example.com no matter from which server it comes
  3. Only use dev.example.com in your code
  1. 我会用 dev.example.com 127.0.0.1 替换主机文件中的“dev”
  2. 我会确保来自 Apache 代理的所有内容都只引用 dev.example.com,无论它来自哪个服务器
  3. 仅在您的代码中使用 dev.example.com

If all else fails you could add a HTTP header 'Access-Control-Allow-Origin: *' to allow any origin, but I would not use this except just in dev-environments.

如果所有其他方法都失败了,您可以添加一个 HTTP 标头 'Access-Control-Allow-Origin: *' 以允许任何来源,但除了在开发环境中之外,我不会使用它。

PS. Even if you get javascript from example.com:80 that javascript can't even call example.com:443 or javascript from example.com cannot make xmlhttprequests to dev.example.com

附注。即使您从 example.com:80 获得 javascript,该 javascript 甚至无法调用 example.com:443 或来自 example.com 的 javascript 也无法向 dev.example.com 发出 xmlhttprequests

回答by HornedReaper

on 127.0.0.1, your html code should be:

在 127.0.0.1 上,你的 html 代码应该是:

$.ajax({
    url: "http://127.0.0.1/a/w/currencies",
    type: "GET",
    dataType: "json",
    data: {
    },
    success: function(data){
        console.log(data);
    }
}); 

on 127.0.0.1, your apache conf should be:

在 127.0.0.1 上,你的 apache conf 应该是:

...

<VirtualHost dev:8080>
            ...
            ProxyPass / https://app.somesite.com:5002/
            ProxyPassReverse / https://app.somesitecom:5002/
            ...
</VirtualHost>

on this case, your browser will not cross-domain, because your url and ajax use the same domain.But exactly, ajax request https://app.somesite.com:5002/, I don't know if it is a reverse-proxy ,but it seems work for me. Have a try :)

在这种情况下,你的浏览器不会跨域,因为你的url和ajax使用的是同一个域。但确切地说,ajax请求 https://app.somesite.com:5002/,不知道是不是反向-proxy ,但它似乎对我有用。试试 :)

回答by olso4539

I will be attempting the same thing myself pretty soon which is how I found this thread.

我很快就会自己尝试同样的事情,这就是我发现这个线程的方式。

I wonder if the actual url that you are using in the AJAX request is wrong. Essentially, you are connecting to the proxy. It is forwarding you to the port 8080 address. Then you try to make an AJAX request directly to the 8080 address? A relative link may work so that the AJAX call gets forwarded along the same path so that javascript knows it's the same origin.

我想知道您在 AJAX 请求中使用的实际 url 是否错误。本质上,您正在连接到代理。它会将您转发到端口 8080 地址。那你试试直接向8080地址发出AJAX请求?相对链接可能会起作用,以便 AJAX 调用沿着相同的路径转发,以便 javascript 知道它的来源相同。

An alternative is backending with PHP. Lecture 7 of this online course covers AJAX and does an example with PHP to completely circumvent same origin restrictions. http://academicearth.org/courses/building-dynamic-websites/

另一种方法是使用 PHP 进行后端。本在线课程的第 7 讲涵盖了 AJAX,并使用 PHP 进行了示例以完全规避同源限制。 http://academicearth.org/courses/building-dynamic-websites/

I just found this, it seems like a better solution. http://darius.kruythoff.net/blog/2011/xss-with-apache/

我刚刚找到了这个,这似乎是一个更好的解决方案。 http://darius.kruythoff.net/blog/2011/xss-with-apache/