解决Swagger部署在服务器访问错误
问题背景
最近在部署项目到服务器上时,访问Swagger的API文档服务时遇到了一个典型问题:在自己电脑本地环境下,Swagger是正常的,数据也能展示;但在服务器环境下,Swagger UI界面能够正常加载,但无法获取API的元数据信息。
问题定位
1. 前端现象观察
在前端项目中我们通过iframe组件访问 /prod-api/swagger-ui/index.html 打开Swagger UI界面时,报错:

如果了解过Swagger的话,能访问到这个页面,基本可以确定,是获取元数据的问题了。
为什么?因为Swagger UI界面是需要请求后端静态资源获取的,这证明静态资源可以访问,但数据却不是我们要展示的数据,要知道展示的数据也是需要请求后端获取的。这些流程Swagger已经帮我们完成了,那么就两种可能,一种是前端的请求没有发送到后端,另一种就是请求发送到后端了,但是被拦截掉了,如果是被拦截的,我们就需要知道是哪个接口,然后在后端进行放行。我们来看请求

index.html就是我们浏览器访问地址获取的资源文件,那其他的请求是怎么回事?我们来看一下这个html做了什么

在访问这个html的时候,它会去获取这三个js文件,前两个js文件不必理会,不是问题的关键,我们看第三个,也就是swagger-initializer.js

可以看到,swagger-initializer.js文件会在窗口加载时,去初始化SwaggerUI,SwaggerUIBundle其实就是前面的swagger-ui-bundle.js,这个js具体做了什么我们不需要知道,看请求的顺序就可以明白:
- 先请求configUrl,也就是https://域名/v3/api-docs/swagger-config,获取到swagger的配置信息数据
- 如果获取不到swagger-config的数据,则请求https://petstore.swagger.io/v2/swagger.json获取数据进行展示


可以看到,事实确实如此,那么如何解决呢?我们先确定下是不是后端的问题
2. 后端服务检查
Swagger配置
# Springdoc配置
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
tags-sorter: alpha
# 配置分组将不再显示所有接口,而是根据分组进行显示
group-configs:
- group: 'common'
display-name: '公共模块'
paths-to-match: '/**'
packages-to-scan: com.lucky.web.controller.common
- group: 'monitor'
display-name: '监控模块'
paths-to-match: '/**'
packages-to-scan: com.lucky.web.controller.monitor
- group: 'system'
display-name: '系统模块'
paths-to-match: '/**'
packages-to-scan: com.lucky.web.controller.system
看下后端的接口拦截规则
// 注解标记允许匿名访问的url
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.requestMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问
.requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll()
.requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})
静态资源我们都已经放行了,所以我们在访问Swagger的时候,才会有Swagger UI界面展示出来。细心的朋友已经发现了,没错,/v3/api-docs/** 接口我们也已经放行了,也就是我们上面说到的 /v3/api-docs/swagger-config 展示数据的接口,那么就不存在被后端拦截的情况了。我们在浏览器直接访问Swagger的数据接口 https://公网ip:后端项目端口/v3/api-docs/swagger-config

返回的JSON数据,就是我们后端配置的group-configs,说明后端服务本身没有问题。那么就是前端的请求没有发送到后端,我们来看服务器的nginx配置
location / {
try_files $uri $uri/ /index.html;
}
location ^~ /prod-api/ {
# 结尾的斜杠,会将 /prod-api/ 替换成 /
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host; # 传递原始 Host 头
proxy_set_header X-Real-IP $remote_addr; # 传递客户端 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 传递协议 (http/https)
}
看到这里,是不是全都明白了,我们在访问 /prod-api/swagger-ui/index.html 时是有 /prod-api/ 前缀的,并且后续请求的js也都有这个前缀,所以这些请求都能够抵达后端进行响应。唯独 /v3/api-docs/swagger-config 接口是没有 /prod-api/ 前缀的,所以nginx没有将其代理到后端。
解决方案
修改Nginx配置,添加精确匹配规则:
location / {
try_files $uri $uri/ /index.html;
}
location ^~ /prod-api/ {
# 结尾的斜杠,会将 /prod-api/ 替换成 /
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host; # 传递原始 Host 头
proxy_set_header X-Real-IP $remote_addr; # 传递客户端 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 传递协议 (http/https)
}
location ^~ /v3/api-docs/ {
# 结尾没有斜杠,保留 /v3/api-docs/(.*)
proxy_pass http://127.0.0.1:8080;
}
配置解析
^~前缀匹配:确保优先匹配该路径- 结尾无斜杠:保留完整路径结构,使
/v3/api-docs/swagger-config也能被代理
验证效果
重载Nginx配置后:
前端项目再次通过iframe组件访问 /prod-api/swagger-ui/index.html 成功获取到Swagger配置数据并进行展示。

问题说明
- 为什么在请求 https://域名/v3/api-docs/swagger-config 地址时,预览会出现加载动画?
因为该地址会匹配nginx中的这段配置
location / {
try_files $uri $uri/ /index.html;
}
它会去前端目录中找 /v3/api-docs/swagger-config 这个页面,结果没找到,就会显示一直加载的状态。这个加载的状态是我前端项目自己写的,如果你没有写,那预览会出现空白页面。
- 为什么我自己电脑本地环境中Swagger可以正常显示,但是部署在服务器就不行了?
在前端项目的配置中,我们同样配置了代理
// vite 相关配置
server: {
port: 82,
host: true,
open: true,
proxy: {
[VITE_APP_BASE_API]: {
target: VITE_APP_PROXY_TARGET,
changeOrigin: true,
rewrite: (p) => p.replace(new RegExp('^' + VITE_APP_BASE_API), '')
},
// springdoc proxy
'^/v3/api-docs/(.*)': {
target: VITE_APP_PROXY_TARGET,
changeOrigin: true,
}
}
}
这个配置是我们项目在本地运行时(npm run dev),由vite开发服务器进行代理,只在开发时生效。而前端项目打包后,生成的只是静态文件(HTML、JS、CSS),代理配置不会被打包进去。简单的说,一个是开发环境,一个是生产环境,打包后的文件是纯静态的,这些文件没有运行时的代理能力。所以在前端项目中配置的代理,部署在nginx后,仍然需要配置一次。
总结
通过精确配置Nginx的location规则,我们成功解决了Swagger接口访问失败的问题。关键在于理解:
- Swagger UI需要动态请求后端接口获取元数据
- Nginx需要显式配置特定路径的代理规则
- 路径匹配规则的设计直接影响代理效果
配置完成后,Swagger文档就可以正常展示所有API信息了!










