nginx转发post请求静态页面405
背景:用户支付成功后的回调是个静态页面。由于from表单连续提交是post方式,所以会报405 not allowed 错误。
常识:使用post方式请求js、html这样的静态文件一般的web服务器都会返回405 Method Not Allowed。因为默认情况下,nginx、apache、IIs等web服务无法响应静态页面的post请求,后端用来处理post请求,生产环境中不会有此问题(一般都不允许配置静态页面的post请求)
问题:为什么默认不支持静态页面post请求呢?
首先了解一下post请求方法,post请求一般用于提交表单或上传文件,post请求会导致新资源的建立或旧资源的更改。就安全方面来说(排除url地址的透明性),它对比get请求会有更改资源的情况,有些静态资源是不允许更改的,所以默认情况下web服务器上的静态资源都不允许发起post请求。
网上说的有很多种,重新编译nginx,设置正则匹配可以访问html文件类啊什么的,都不好用,而且基本都是你抄我我抄你,没有实际应用过。乱糟糟,最后本人用下面这种方式解决了问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
upstream domain_uau { server 192.168.6.202:5555 weight=5; } server { listen 80; server_name dev-app-server.sinoif.com; access_log /var/www/logs/nginx/dev-app-server.sinoif-access.log; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-NginX-Proxy true; proxy_pass http://domain_uau/; add_header Cache-Control "no-cache, no-store";#不生成缓存 error_page 405 =200 @fullfuck; proxy_intercept_errors on; } location @fullfuck { proxy_method GET; proxy_pass http://192.168.6.202:5555; } } #error_page 405 =200 @fullfuck 或者 error_page 405 = @fullfuck; 2种写法都可以 #proxy_intercept_errors当上游服务器响应头回来后,可以根据响应状态码的值进行拦截错误处理,与error_page 指令相互结合。用在访问上游服务器出现错误的情况下,这个参数一定要带上并且开启on,否则下边不生效 @:定义命名location区段,这些区段客户段不能访问,只可以由内部产生的请 求来访问,如try_files或error_page等 逻辑就是如果post请求返回了405错误,那么定义405区段请求为GET方式,注意 error_page 要写在location里,而不是server里否则不生效 |
Nginx配置跨域请求 Access-Control-Allow-Origin *
当出现403跨域错误的时候 No 'Access-Control-Allow-Origin' header is present on the requested resource
,需要给Nginx服务器配置响应的header参数:
一、 解决方案
只需要在Nginx的配置文件中配置以下参数:
1 2 3 4 5 6 7 8 9 |
location / { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; if ($request_method = 'OPTIONS') { return 204; } } |
二、 解释
1. Access-Control-Allow-Origin
1 |
服务器默认是不被允许跨域的。给Nginx服务器配置`Access-Control-Allow-Origin *`后,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。 |
2. Access-Control-Allow-Headers 是为了防止出现以下错误:
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
这个错误表示当前请求Content-Type的值不被支持。其实是我们发起了”application/json”的类型请求导致的。这里涉及到一个概念:预检请求(preflight request)
,请看下面”预检请求”的介绍。
3. Access-Control-Allow-Methods 是为了防止出现以下错误:
Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
4.给OPTIONS
添加 204
的返回,是为了处理在发送POST请求时Nginx依然拒绝访问的错误
发送”预检请求”时,需要用到方法 OPTIONS
,所以服务器需要允许该方法。
三、 预检请求(preflight request)
其实上面的配置涉及到了一个W3C标准:CROS
,全称是跨域资源共享 (Cross-origin resource sharing),它的提出就是为了解决跨域请求的。
跨域资源共享(CORS)标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,
对那些可能对服务器数据产生副作用的HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求)
,浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
其实Content-Type字段的类型为application/json
的请求就是上面所说的搭配某些 MIME 类型的 POST 请求
,CORS规定,Content-Type不属于以下MIME类型的,都属于预检请求:
1 2 3 |
application/x-www-form-urlencoded multipart/form-data text/plain |
所以 application/json的请求 会在正式通信之前,增加一次”预检”请求,这次”预检”请求会带上头部信息 Access-Control-Request-Headers: Content-Type
:
1 2 3 4 5 |
OPTIONS /api/test HTTP/<span class="hljs-number">1.1</span> Origin: http:<span class="hljs-comment">//foo.example</span> Access-Control-Request-<span class="hljs-keyword">Method</span>: POST Access-Control-Request-Headers: Content-<span class="hljs-keyword">Type</span> ... 省略了一些 |
服务器回应时,返回的头部信息如果不包含Access-Control-Allow-Headers: Content-Type
则表示不接受非默认的的Content-Type。即出现以下错误:
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
其他nginx知识(随笔)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
1.proxy_set_header Host $host :这一行的作用是把原http请求的Header中的Host字段也放到转发的请求里。如果不加这一行的话,nginx转发的请求header里就不会有Host字段,而服务器是靠这个Host值来区分你请求的是哪个域名的资源的。 3.proxy_set_header X-NginX-Proxy true :应该是代理转发的意思 true转发 2.proxy_set_header X-Real-IP $remote_addr 和 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 这2个要放一起说: #$proxy_add_x_forwarded_for #$http_x_forwarded_for 这两个的变量的值的区别,就在于,proxy_add_x_forwarded_for 比 http_x_forwarded_for 多了一个$remote_addr的值 但是$remote_addr 只能获取到与服务器本身直连的上层请求ip,所以设置$remote_addr一般都是设置第一个代理上面 但是问题是,有时候是通过cdn访问过来的,那么后面web服务器获取到的,永远都是cdn 的ip 而非真是用户ip 那么这个时候就要用到X-FORward—for了,这个变量的意思,其实就像是链路反追踪,从客户的真实ip为起点,穿过多层级的proxy ,最终到达web 服务器,都会记录下来,所以在获取用户真实ip的时候,一般就可以设置成,proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 这样就能获取所有的代理ip 客户ip 在打印log 的时候, #$http_x_real_ip|$remote_addr 就是用户的真实IP 配置如下 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 还有一种特殊情况就是 客户在经过cdn请求的时候,本来$proxy_add_x_forwarded_for这里记录的值都全部都包括,但是,当你需要取值的时候,会发现,即便用排除代理ip模块 set_real_ip_from 100.0.0.0/8;(这里是已知的代理ip) real_ip_header X-Forwarded-For; real_ip_recursive on; 也会导致 X-Forwarded-For 里依然有多个ip,这个时候直接取值$http_x_real_ip 就好了,但是前提条件是,cdn 那边也设置了X-forward,不然,你这边获取的你认为是用户的ip 其实是cdn的ip |
另: https://www.mryunwei.com/2769.html
0 Comments