使用 Nginx 转发请求。把跨域的接口写成调本域的接口,然后将这些接口转发到真正的请求地址。
原文: 别整复杂了!Nginx 可以轻松搞定跨域问题
跨域主要涉及4个响应头:
- Access-Control-Allow-Origin 用于设置允许跨域请求源地址 (预检请求和正式请求在跨域时候都会验证)
- Access-Control-Allow-Headers 跨域允许携带的特殊头信息字段 (只在预检请求验证)
- Access-Control-Allow-Methods 跨域允许的请求方法或者说HTTP动词 (只在预检请求验证)
- Access-Control-Allow-Credentials 是否允许跨域使用cookies,如果要跨域使用cookies,可以添加上此请求响应头,值设为true(设置或者不设置,都不会影响请求发送,只会影响在跨域时候是否要携带cookies,但是如果设置,预检请求和正式请求都需要设置)。不过不建议跨域使用(项目中用到过,不过不稳定,有些浏览器带不过去),除非必要,因为有很多方案可以代替。
准备:
前端网站地址:http://localhost:8080
服务端网址:http://localhost:59200
nginx配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
server {
listen 22222;
server_name localhost;
location / {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Methods '*';
add_header Access-Control-Allow-Credentials 'true';
return 204;
}
if ($request_method != 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
add_header Access-Control-Allow-Credentials 'true';
}
proxy_pass http://localhost:59200;
}
}
|
配置实践
- 先启动一个http服务
golang 实现的一个简单的http服务示例,使用http.ServeMux控制路由的HTTP服务器。
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
|
package main
import (
"fmt"
"net/http"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
}
func aboutHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is the about page.")
}
func main() {
// 创建一个ServeMux对象
mux := http.NewServeMux()
// 注册处理函数和路径到ServeMux
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/about", aboutHandler)
// 使用ServeMux启动服务器
fmt.Println("Server is listening on port 8080...")
if err := http.ListenAndServe(":8080", mux); err != nil {
fmt.Printf("Error starting server: %s\n", err)
}
}
|
直接运行go run main.go
需要代理的http服务,监听8080端口
启动配置nginx服务
- 创建一个nginx.conf文件,跨域配置如下
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
|
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost';
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS, PUT, DELETE';
add_header Access-Control-Allow-Credentials 'true';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method != 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost' always;
add_header Access-Control-Allow-Credentials 'true';
}
proxy_pass http://localhost:8080;
}
}
|
在docker中启动nginx
- 使用
bitnami/nginx:1.25.5-debian-12-r1
镜像启动nginx服务
1
2
3
4
5
6
|
docker run -d -p 80:80 --name my-nginx-cors -v ./nginx.conf:/etc/nginx/nginx.conf:ro bitnami/nginx:1.25.5-debian-12-r1
ad5be6375aa5aee5ee524c51d0586d00fe622347cb65eafa0af26e5afda59438
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad5be6375aa5 bitnami/nginx:1.25.5-debian-12-r1 "/opt/bitnami/script…" 5 seconds ago Up 4 seconds 8080/tcp, 0.0.0.0:80->80/tcp, :::80->80/tcp, 8443/tcp my-nginx-cors
|
- 由于这里用的是bitnami/nginx镜像,配置文件位置不一样,bitnami/nginx默认配置如下
查看配置文件内容
1
|
docker exec -it my-nginx-cors cat /opt/bitnami/nginx/conf/nginx.conf
|
默认配置文件内容如下
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
# Based on https://www.nginx.com/resources/wiki/start/topics/examples/full/#nginx-conf
# user www www; ## Default: nobody
worker_processes auto;
error_log "/opt/bitnami/nginx/logs/error.log";
pid "/opt/bitnami/nginx/tmp/nginx.pid";
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log "/opt/bitnami/nginx/logs/access.log" main;
add_header X-Frame-Options SAMEORIGIN;
client_body_temp_path "/opt/bitnami/nginx/tmp/client_body" 1 2;
proxy_temp_path "/opt/bitnami/nginx/tmp/proxy" 1 2;
fastcgi_temp_path "/opt/bitnami/nginx/tmp/fastcgi" 1 2;
scgi_temp_path "/opt/bitnami/nginx/tmp/scgi" 1 2;
uwsgi_temp_path "/opt/bitnami/nginx/tmp/uwsgi" 1 2;
sendfile on;
tcp_nopush on;
tcp_nodelay off;
gzip on;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_proxied any;
gzip_types text/plain text/css application/javascript text/xml application/xml+rss;
keepalive_timeout 65;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
client_max_body_size 80M;
server_tokens off;
absolute_redirect off;
port_in_redirect off;
include "/opt/bitnami/nginx/conf/server_blocks/*.conf";
# HTTP Server
server {
# Port to listen on, can also be set in IP:PORT format
listen 8080;
include "/opt/bitnami/nginx/conf/bitnami/*.conf";
location /status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}
}
|
所以直接替换这个配置文件,再启动服务。
- 修改其中http server部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
server {
listen 80;
server_name localhost;
location / {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost';
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS, PUT, DELETE';
add_header Access-Control-Allow-Credentials 'true';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method != 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost' always;
add_header Access-Control-Allow-Credentials 'true';
}
proxy_pass http://172.20.10.3:8080;
}
}
|
注意这里的proxy_pass代理的地址要填宿主主机的ip地址,因为上面启动的http服务是直接运行在宿主机上
可以用ifconfig
查看宿主机本地ip,这里查到是172.20.10.3。
- 修改后启动或重新加载配置
1
2
3
|
docker run -d -p 80:80 --name my-nginx-cors -v ./nginx.conf:/opt/bitnami/nginx/conf/nginx.conf:ro bitnami/nginx:1.25.5-debian-12-r1
docker exec -it my-nginx-cors nginx -s reload
|
测试跨域请求
1
2
3
4
5
6
7
8
9
10
11
12
|
curl -I -X OPTIONS -H "Origin: http://example.com" http://localhost
HTTP/1.1 204 No Content
Server: nginx
Date: Sun, 25 May 2025 11:46:07 GMT
Connection: keep-alive
Access-Control-Allow-Origin: http://localhost
Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
Content-Type: text/plain charset=UTF-8
Content-Length: 0
|
看到响应的Access-Control-Allow-Origin: http://localhost
,这样表示在浏览器中存在跨域。
此时就需要修改为Access-Control-Allow-Origin: http://example.com
域名来解决跨域问题。
修改之后再访问就可以看到已经发生变化。
允许多个域名跨域访问
由于Access-Control-Allow-Origin参数只允许配置单个域名或者*,当需要允许多个域名跨域访问时可以用以下方法来实现。
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
37
38
39
40
|
map $http_origin $corsHost {
default 0;
"~http://www.example.com" http://www.example.com;
"~http://example.com" http://example.com;
}
# HTTP Server
server {
# Port to listen on, can also be set in IP:PORT format
listen 80;
server_name localhost;
include "/opt/bitnami/nginx/conf/bitnami/*.conf";
location /status {
stub_status on;
access_log off;
#allow 127.0.0.1;
#deny all;
}
location / {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin $corsHost;
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS, PUT, DELETE';
add_header Access-Control-Allow-Credentials 'true';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method != 'OPTIONS') {
add_header Access-Control-Allow-Origin $corsHost always;
add_header Access-Control-Allow-Credentials 'true';
}
proxy_pass http://172.20.10.3:8080;
}
}
|
这样就配置允许两个域名跨域访问了。
参考